/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (StdEnumDateElement.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.calendar.service; import net.time4j.engine.AttributeQuery; import net.time4j.engine.ChronoDisplay; import net.time4j.engine.ChronoEntity; import net.time4j.engine.ChronoOperator; import net.time4j.format.Attributes; import net.time4j.format.CalendarText; import net.time4j.format.CalendarType; import net.time4j.format.NumericalElement; import net.time4j.format.OutputContext; import net.time4j.format.TextAccessor; import net.time4j.format.TextElement; import net.time4j.format.TextWidth; import java.io.IOException; import java.text.ParsePosition; import java.util.Locale; import static net.time4j.format.CalendarText.ISO_CALENDAR_TYPE; /** * <p>General enum-based date element. </p> * * @author Meno Hochschild * @since 3.5/4.3 */ /*[deutsch] * <p>Allgemeines Enum-basiertes Datumselement. </p> * * @author Meno Hochschild * @since 3.5/4.3 */ public class StdEnumDateElement<V extends Enum<V>, T extends ChronoEntity<T>> extends StdDateElement<V, T> implements NumericalElement<V>, TextElement<V> { //~ Statische Felder/Initialisierungen -------------------------------- private static final long serialVersionUID = -2452569351302286113L; //~ Instanzvariablen -------------------------------------------------- private transient final Class<V> type; private transient final String defaultCalendarType; private transient final ChronoOperator<T> decrementor; private transient final ChronoOperator<T> incrementor; //~ Konstruktoren ----------------------------------------------------- public StdEnumDateElement( String name, Class<T> chrono, Class<V> type, char symbol ) { super(name, chrono, symbol, isWeekdayElement(symbol)); this.type = type; this.defaultCalendarType = extractCalendarType(chrono); this.decrementor = null; this.incrementor = null; } public StdEnumDateElement( String name, Class<T> chrono, Class<V> type, char symbol, String defaultCalendarType ) { super(name, chrono, symbol, isWeekdayElement(symbol)); this.type = type; this.defaultCalendarType = defaultCalendarType; this.decrementor = null; this.incrementor = null; } public StdEnumDateElement( String name, Class<T> chrono, Class<V> type, char symbol, ChronoOperator<T> decrementor, ChronoOperator<T> incrementor ) { super(name, chrono, symbol, false); this.type = type; this.defaultCalendarType = extractCalendarType(chrono); this.decrementor = decrementor; this.incrementor = incrementor; } //~ Methoden ---------------------------------------------------------- @Override public Class<V> getType() { return this.type; } @Override public V getDefaultMinimum() { return this.type.getEnumConstants()[0]; } @Override public V getDefaultMaximum() { V[] enums = this.type.getEnumConstants(); return enums[enums.length - 1]; } @Override public int numerical(V value) { return value.ordinal() + 1; } @Override public ChronoOperator<T> decremented() { if (this.decrementor != null) { return this.decrementor; } return super.decremented(); } @Override public ChronoOperator<T> incremented() { if (this.incrementor != null) { return this.incrementor; } return super.incremented(); } @Override public void print( ChronoDisplay context, Appendable buffer, AttributeQuery attributes ) throws IOException { V value = context.get(this); OutputContext oc = attributes.get(Attributes.OUTPUT_CONTEXT, OutputContext.FORMAT); buffer.append(this.accessor(attributes, oc, isLeapMonth(value)).print(value)); } @Override public V parse( CharSequence text, ParsePosition status, AttributeQuery attributes ) { int index = status.getIndex(); OutputContext oc = attributes.get(Attributes.OUTPUT_CONTEXT, OutputContext.FORMAT); V result = this.accessor(attributes, oc, false).parse(text, status, this.getType(), attributes); if ((result == null) && this.isMonthElement()) { status.setErrorIndex(-1); status.setIndex(index); result = this.accessor(attributes, oc, true).parse(text, status, this.getType(), attributes); } if ((result == null) && attributes.get(Attributes.PARSE_MULTIPLE_CONTEXT, Boolean.TRUE)) { status.setErrorIndex(-1); status.setIndex(index); oc = ((oc == OutputContext.FORMAT) ? OutputContext.STANDALONE : OutputContext.FORMAT); result = this.accessor(attributes, oc, false).parse(text, status, this.getType(), attributes); if ((result == null) && this.isMonthElement()) { status.setErrorIndex(-1); status.setIndex(index); result = this.accessor(attributes, oc, true).parse(text, status, this.getType(), attributes); } } return result; } /** * <p>Does given element value represent a leap form? </p> * * <p>Example: If given value is the hebrew month ADAR-I or ADAR-II * then this method must return {@code true}. </p> * * @param value element value * @return {@code false} by default * @since 3.5/4.3 */ protected boolean isLeapMonth(V value) { return false; } /** * <p>Does this element represent a calendar era? </p> * * <p>The default implementation returns {@code true} * if and only if the associated format pattern symbol is G. </p> * * @return boolean * @since 3.5/4.3 */ protected boolean isEraElement() { return (this.getSymbol() == 'G'); } /** * <p>Does this element represent a calendar month? </p> * * <p>The default implementation returns {@code true} * if and only if the associated format pattern symbol is M. </p> * * @return boolean * @since 3.5/4.3 */ protected boolean isMonthElement() { return (this.getSymbol() == 'M'); } /** * <p>Does this element represent a day of any calendar week? </p> * * <p>The default implementation returns {@code true} * if and only if the associated format pattern symbol is E. </p> * * @return boolean * @since 3.5/4.3 */ protected boolean isWeekdayElement() { return isWeekdayElement(this.getSymbol()); } private static boolean isWeekdayElement(char symbol) { return (symbol == 'E'); } private static String extractCalendarType(Class<?> type) { CalendarType ft = type.getAnnotation(CalendarType.class); return ((ft == null) ? ISO_CALENDAR_TYPE : ft.value()); } private TextAccessor accessor( AttributeQuery attributes, OutputContext outputContext, boolean leap ) { Locale lang = attributes.get(Attributes.LANGUAGE, Locale.ROOT); TextWidth textWidth = attributes.get(Attributes.TEXT_WIDTH, TextWidth.WIDE); CalendarText cnames; if (this.isMonthElement()) { cnames = CalendarText.getInstance( attributes.get(Attributes.CALENDAR_TYPE, this.defaultCalendarType), lang); if (leap) { return cnames.getLeapMonths(textWidth, outputContext); } else { return cnames.getStdMonths(textWidth, outputContext); } } else if (this.isWeekdayElement()) { cnames = CalendarText.getIsoInstance(lang); return cnames.getWeekdays(textWidth, outputContext); } else if (this.isEraElement()) { cnames = CalendarText.getInstance( attributes.get(Attributes.CALENDAR_TYPE, this.defaultCalendarType), lang); return cnames.getEras(textWidth); } else { return CalendarText.getInstance(this.defaultCalendarType, lang).getTextForms(this.name(), this.type); } } }