/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (CalendarMonth.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.range;
import net.time4j.CalendarUnit;
import net.time4j.Moment;
import net.time4j.Month;
import net.time4j.PlainDate;
import net.time4j.SystemClock;
import net.time4j.base.GregorianMath;
import net.time4j.base.MathUtils;
import net.time4j.base.TimeSource;
import net.time4j.engine.AttributeQuery;
import net.time4j.engine.BridgeChronology;
import net.time4j.engine.ChronoElement;
import net.time4j.engine.ChronoEntity;
import net.time4j.engine.ChronoMerger;
import net.time4j.engine.Chronology;
import net.time4j.engine.Converter;
import net.time4j.engine.DisplayStyle;
import net.time4j.engine.ElementRule;
import net.time4j.engine.FormattableElement;
import net.time4j.engine.IntElementRule;
import net.time4j.engine.ThreetenAdapter;
import net.time4j.engine.ValidationElement;
import net.time4j.format.Attributes;
import net.time4j.format.CalendarText;
import net.time4j.format.CalendarType;
import net.time4j.format.Leniency;
import net.time4j.format.LocalizedPatternSupport;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.PatternType;
import net.time4j.tz.Timezone;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.time.YearMonth;
import java.time.chrono.IsoChronology;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* <p>Represents the month of a gregorian calendar year as interval
* (like from 1st of January until end of January). </p>
*
* <p>The elements registered by this class are: </p>
*
* <ul>
* <li>{@link #YEAR}</li>
* <li>{@link #MONTH_OF_YEAR}</li>
* <li>{@link #MONTH_AS_NUMBER}</li>
* </ul>
*
* <p>Formatting example for localized styles: </p>
*
* <pre>
* ChronoFormatter<CalendarMonth> usStyle =
* ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.US, CalendarMonth.chronology());
* ChronoFormatter<CalendarMonth> germanStyle =
* ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.GERMANY, CalendarMonth.chronology());
* System.out.println("US-format: " + usStyle.format(CalendarMonth.of(2016, 5))); // US-format: 5/2016
* System.out.println("German: " + germanStyle.format(CalendarMonth.of(2016, 5))); // German: 5.2016
* </pre>
*
* <p>Note: The current month of calendar year can be determined by an expression like:
* {@code CalendarMonth current = SystemClock.inLocalView().now(CalendarMonth.chronology())}. </p>
*
* @author Meno Hochschild
* @since 3.21/4.17
* @doctags.concurrency {immutable}
*/
/*[deutsch]
* <p>Repräsentiert den Monat eines Kalenderjahres als Intervall
* (zum Beispiel vom ersten Januar bis Ende Januar). </p>
*
* <p>Die von dieser Klasse registrierten Elemente sind: </p>
*
* <ul>
* <li>{@link #YEAR}</li>
* <li>{@link #MONTH_OF_YEAR}</li>
* <li>{@link #MONTH_AS_NUMBER}</li>
* </ul>
*
* <p>Formatierungsbeispiel für lokalisierte Formatstile: </p>
*
* <pre>
* ChronoFormatter<CalendarMonth> usStyle =
* ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.US, CalendarMonth.chronology());
* ChronoFormatter<CalendarMonth> germanStyle =
* ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.GERMANY, CalendarMonth.chronology());
* System.out.println("US-format: " + usStyle.format(CalendarMonth.of(2016, 5))); // US-format: 5/2016
* System.out.println("German: " + germanStyle.format(CalendarMonth.of(2016, 5))); // German: 5.2016
* </pre>
*
* <p>Hinweis: Der aktuelle Monat kann mit einem Ausdruck wie folgt bestimmt werden:
* {@code CalendarMonth current = SystemClock.inLocalView().now(CalendarMonth.chronology())}. </p>
*
* @author Meno Hochschild
* @since 3.21/4.17
* @doctags.concurrency {immutable}
*/
@CalendarType("iso8601")
public final class CalendarMonth
extends FixedCalendarInterval<CalendarMonth>
implements ThreetenAdapter, LocalizedPatternSupport {
//~ Statische Felder/Initialisierungen --------------------------------
/**
* <p>Element with the proleptic iso-year without any era reference and
* the value range {@code -999999999} until {@code 999999999}. </p>
* <p/>
* <p>The term "proleptic" means that the rules of the gregorian
* calendar and the associated way of year counting is applied backward
* even before the introduction of gregorian calendar. The year {@code 0}
* is permitted - and negative years, too. For historic year numbers,
* this mathematical extrapolation is not recommended and usually
* wrong. </p>
*/
/*[deutsch]
* <p>Element mit dem proleptischen ISO-Jahr ohne Ära-Bezug mit dem
* Wertebereich {@code -999999999} bis {@code 999999999}. </p>
*
* <p>Der Begriff "proleptisch" bedeutet, daß die Regeln
* des gregorianischen Kalenders und damit verbunden die Jahreszählung
* auch rückwirkend vor der Einführung des Kalenders angewandt
* werden. Insbesondere ist auch das Jahr {@code 0} zugelassen - nebst
* negativen Jahreszahlen. Für historische Jahreszahlen ist diese
* mathematische Extrapolation nicht geeignet. </p>
*/
@FormattableElement(format = "u")
public static final ChronoElement<Integer> YEAR = PlainDate.YEAR;
/**
* <p>Element with the month of year in the value range
* {@code January - December}. </p>
*/
/*[deutsch]
* <p>Element mit dem Monat des Jahres (Wertebereich {@code Januar - Dezember}). </p>
*/
@FormattableElement(format = "M", standalone="L")
public static final ChronoElement<Month> MONTH_OF_YEAR = PlainDate.MONTH_OF_YEAR;
/**
* <p>Element with the month as number in the value range {@code 1 - 12}. </p>
*/
/*[deutsch]
* <p>Element mit dem Monat als Zahl (Wertebereich {@code 1 - 12}). </p>
*/
public static final ChronoElement<Integer> MONTH_AS_NUMBER = PlainDate.MONTH_AS_NUMBER;
private static final Chronology<CalendarMonth> ENGINE =
Chronology.Builder
.setUp(CalendarMonth.class, new Merger())
.appendElement(YEAR, new YearRule())
.appendElement(MONTH_OF_YEAR, new EnumMonthRule())
.appendElement(MONTH_AS_NUMBER, new IntMonthRule())
.build();
private static final ChronoFormatter<CalendarMonth> PARSER =
ChronoFormatter.setUp(CalendarMonth.chronology(), Locale.ROOT)
.addPattern("uuuu-MM|uuuuMM", PatternType.CLDR).build();
private static final Chronology<YearMonth> THREETEN;
static {
Converter<YearMonth, CalendarMonth> converter =
new Converter<YearMonth, CalendarMonth>() {
@Override
public CalendarMonth translate(YearMonth source) {
return CalendarMonth.of(source.getYear(), source.getMonthValue());
}
@Override
public YearMonth from(CalendarMonth time4j) {
return YearMonth.of(time4j.year, time4j.month.getValue());
}
@Override
public Class<YearMonth> getSourceType() {
return YearMonth.class;
}
};
THREETEN = new BridgeChronology<>(converter, ENGINE);
}
private static final long serialVersionUID = -5097347953941448741L;
//~ Instanzvariablen --------------------------------------------------
private transient final int year;
private transient final Month month;
private transient final Boundary<PlainDate> start;
private transient final Boundary<PlainDate> end;
//~ Konstruktoren -----------------------------------------------------
private CalendarMonth(
int year,
Month month
) {
super();
if ((year < GregorianMath.MIN_YEAR) || (year > GregorianMath.MAX_YEAR)) {
throw new IllegalArgumentException("Year out of bounds: " + year);
} else if (month == null) {
throw new NullPointerException("Missing month of calendar year.");
}
this.year = year;
this.month = month;
this.start = Boundary.ofClosed(PlainDate.of(this.year, month, 1));
this.end = Boundary.ofClosed(PlainDate.of(year, month, GregorianMath.getLengthOfMonth(year, month.getValue())));
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Creates a new instance based on given gregorian calendar year and month. </p>
*
* @param year gregorian year within range {@code -999,999,999 / +999,999,999}
* @param month gregorian month in range 1-12
* @return new instance
* @throws IllegalArgumentException if any argument is out of range
*/
/*[deutsch]
* <p>Erzeugt eine neue Instanz mit dem angegebenen gregorianischen Kalendarjahr und Monat. </p>
*
* @param year gregorian year within range {@code -999,999,999 / +999,999,999}
* @param month gregorian month in range 1-12
* @return new instance
* @throws IllegalArgumentException if any argument is out of range
*/
public static CalendarMonth of(
int year,
int month
) {
return new CalendarMonth(year, Month.valueOf(month));
}
/**
* <p>Creates a new instance based on given gregorian calendar year and month. </p>
*
* @param year gregorian year within range {@code -999,999,999 / +999,999,999}
* @param month gregorian month
* @return new instance
* @throws IllegalArgumentException if given year is out of range
*/
/*[deutsch]
* <p>Erzeugt eine neue Instanz mit dem angegebenen gregorianischen Kalendarjahr und Monat. </p>
*
* @param year gregorian year within range {@code -999,999,999 / +999,999,999}
* @param month gregorian month
* @return new instance
* @throws IllegalArgumentException if given year is out of range
*/
public static CalendarMonth of(
int year,
Month month
) {
return new CalendarMonth(year, month);
}
/**
* <p>Obtains the current calendar month in system time. </p>
*
* <p>Convenient short-cut for: {@code SystemClock.inLocalView().now(CalendarMonth.chronology())}. </p>
*
* @return current calendar month in system time zone using the system clock
* @see SystemClock#inLocalView()
* @see net.time4j.ZonalClock#now(net.time4j.engine.Chronology)
* @since 3.24/4.20
*/
/*[deutsch]
* <p>Ermittelt den aktuellen Kalendermonat in der Systemzeit. </p>
*
* <p>Bequeme Abkürzung für: {@code SystemClock.inLocalView().now(CalendarMonth.chronology())}. </p>
*
* @return current calendar month in system time zone using the system clock
* @see SystemClock#inLocalView()
* @see net.time4j.ZonalClock#now(net.time4j.engine.Chronology)
* @since 3.24/4.20
*/
public static CalendarMonth nowInSystemTime() {
return SystemClock.inLocalView().now(CalendarMonth.chronology());
}
/**
* <p>Combines this year and month with given day of month to a calendar date. </p>
*
* @param dayOfMonth day of month in range 1-28/29/30/31
* @return calendar date
* @throws IllegalArgumentException if the day-of-month is out of range
*/
/*[deutsch]
* <p>Kombiniert diese Instanz mit dem angegebenen Tag zu einem Kalenderdatum. </p>
*
* @param dayOfMonth day of month in range 1-28/29/30/31
* @return calendar date
* @throws IllegalArgumentException if the day-of-month is out of range
*/
public PlainDate atDayOfMonth(int dayOfMonth) {
if (dayOfMonth == 1) {
return this.start.getTemporal();
}
return this.start.getTemporal().with(PlainDate.DAY_OF_MONTH, dayOfMonth);
}
/**
* <p>Yields the date of the end of this calendar month. </p>
*
* @return PlainDate
*/
/*[deutsch]
* <p>Liefert das Endedatum dieses Kalendermonats. </p>
*
* @return PlainDate
*/
public PlainDate atEndOfMonth() {
return this.end.getTemporal();
}
/**
* <p>Yields the year number. </p>
*
* @return int
*/
/*[deutsch]
* <p>Liefert die Jahreszahl. </p>
*
* @return int
*/
public int getYear() {
return this.year;
}
/**
* <p>Yields the month as enum. </p>
*
* <p>User who wish to get the month as integer can use the expression {@code getMonth().getValue()}. </p>
*
* @return Month
*/
/*[deutsch]
* <p>Liefert den Monat als enum-Wert. </p>
*
* <p>Anwender, die den Monat als Zahl erhalten wollen, können den Ausdruck
* {@code getMonth().getValue()} verwenden. </p>
*
* @return Month
*/
public Month getMonth() {
return this.month;
}
@Override
public Boundary<PlainDate> getStart() {
return this.start;
}
@Override
public Boundary<PlainDate> getEnd() {
return this.end;
}
@Override
public boolean contains(PlainDate temporal) {
return ((temporal.getYear() == this.year) && (temporal.getMonth() == this.month.getValue()));
}
@Override
public boolean isAfter(PlainDate temporal) {
return this.start.getTemporal().isAfter(temporal);
}
@Override
public boolean isBefore(PlainDate temporal) {
return this.end.getTemporal().isBefore(temporal);
}
/**
* <p>Determines the count of days belonging to this month. </p>
*
* @return int
*/
/**
* <p>Ermittelt die Anzahl der Tage, die zu diesem Kalendermonat gehören. </p>
*
* @return int
*/
public int length() {
return GregorianMath.getLengthOfMonth(this.year, this.month.getValue());
}
/**
* <p>Converts given JSR-310-type to a calendar month. </p>
*
* @param yearMonth Threeten-equivalent of this instance
* @return CalendarMonth
* @see #toTemporalAccessor()
*/
/*[deutsch]
* <p>Konvertiert den angegebenen JSR-310-Typ zu einem Kalendermonat. </p>
*
* @param yearMonth Threeten-equivalent of this instance
* @return CalendarMonth
* @see #toTemporalAccessor()
*/
public static CalendarMonth from(YearMonth yearMonth) {
return CalendarMonth.of(yearMonth.getYear(), yearMonth.getMonthValue());
}
/**
* <p>Adds given years to this calendar month. </p>
*
* @param years the count of years to be added
* @return result of addition
*/
/*[deutsch]
* <p>Addiert die angegebenen Jahre zu diesem Kalendermonat. </p>
*
* @param years the count of years to be added
* @return result of addition
*/
public CalendarMonth plus(Years<CalendarUnit> years) {
if (years.isEmpty()) {
return this;
}
return CalendarMonth.of(MathUtils.safeAdd(this.year, years.getAmount()), this.month);
}
/**
* <p>Adds given months to this calendar month. </p>
*
* @param months the count of months to be added
* @return result of addition
*/
/*[deutsch]
* <p>Addiert die angegebenen Monate zu diesem Kalendermonat. </p>
*
* @param months the count of months to be added
* @return result of addition
*/
public CalendarMonth plus(Months months) {
if (months.isEmpty()) {
return this;
}
long value = this.year * 12L + this.month.getValue() - 1 + months.getAmount();
int y = MathUtils.safeCast(MathUtils.floorDivide(value, 12));
Month m = Month.valueOf(MathUtils.floorModulo(value, 12) + 1);
return CalendarMonth.of(y, m);
}
/**
* <p>Subtracts given years from this calendar month. </p>
*
* @param years the count of years to be subtracted
* @return result of subtraction
*/
/*[deutsch]
* <p>Subtrahiert die angegebenen Jahre von diesem Kalendermonat. </p>
*
* @param years the count of years to be subtracted
* @return result of subtraction
*/
public CalendarMonth minus(Years<CalendarUnit> years) {
if (years.isEmpty()) {
return this;
}
return CalendarMonth.of(MathUtils.safeSubtract(this.year, years.getAmount()), this.month);
}
/**
* <p>Subtracts given months from this calendar month. </p>
*
* @param months the count of months to be subtracted
* @return result of subtraction
*/
/*[deutsch]
* <p>Subtrahiert die angegebenen Monate von diesem Kalendermonat. </p>
*
* @param months the count of months to be subtracted
* @return result of subtraction
*/
public CalendarMonth minus(Months months) {
if (months.isEmpty()) {
return this;
}
long value = this.year * 12L + this.month.getValue() - 1 - months.getAmount();
int y = MathUtils.safeCast(MathUtils.floorDivide(value, 12));
Month m = Month.valueOf(MathUtils.floorModulo(value, 12) + 1);
return CalendarMonth.of(y, m);
}
@Override
public int compareTo(CalendarMonth other) {
if (this.year < other.year) {
return -1;
} else if (this.year > other.year) {
return 1;
} else {
return this.month.compareTo(other.month);
}
}
/**
* <p>Iterates over all days of this calendar month. </p>
*
* @return Iterator
*/
/*[deutsch]
* <p>Iteratiert über alle Tage dieses Kalendermonats. </p>
*
* @return Iterator
*/
@Override
public Iterator<PlainDate> iterator() {
return new Iter();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof CalendarMonth) {
CalendarMonth that = (CalendarMonth) obj;
return ((this.year == that.year) && (this.month == that.month));
} else {
return false;
}
}
@Override
public int hashCode() {
return this.year ^ this.month.hashCode();
}
/**
* <p>Outputs this instance as a String in CLDR-format "uuuu-MM" (like "2016-10"). </p>
*
* @return String
* @see #parseISO(String)
*/
/*[deutsch]
* <p>Gibt diese Instanz als String im CLDR-Format "uuuu-MM" (wie "2016-10") aus. </p>
*
* @return String
* @see #parseISO(String)
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
formatYear(sb, this.year);
sb.append('-');
int m = this.month.getValue();
if (m < 10) {
sb.append('0');
}
sb.append(m);
return sb.toString();
}
/**
* <p>Interpretes given ISO-conforming text as calendar month. </p>
*
* <p>The underlying parser uses the CLDR-pattern "uuuu-MM|uuuuMM". </p>
*
* @param text text to be parsed
* @return parsed calendar month
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
* @see #toString()
*/
/*[deutsch]
* <p>Interpretiert den angegebenen ISO-kompatiblen Text als Kalendermonat. </p>
*
* <p>Der zugrundeliegende Interpretierer verwendet das CLDR-Formatmuster "uuuu-MM|uuuuMM". </p>
*
* @param text text to be parsed
* @return parsed calendar month
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
* @see #toString()
*/
public static CalendarMonth parseISO(String text) throws ParseException {
return PARSER.parse(text);
}
@Override
public YearMonth toTemporalAccessor() {
return YearMonth.of(this.year, this.month.getValue());
}
/**
* <p>Yields the associated chronology. </p>
*
* @return the underlying rule engine
*/
/*[deutsch]
* <p>Liefert die assoziierte Chronologie. </p>
*
* @return the underlying rule engine
*/
public static Chronology<CalendarMonth> chronology() {
return ENGINE;
}
/**
* <p>Obtains a bridge chronology for the type {@code java.time.YearMonth}. </p>
*
* @return rule engine adapted for the type {@code java.time.YearMonth}
* @see #chronology()
*/
/*[deutsch]
* <p>Liefert eine an den Typ {@code java.time.YearMonth} angepasste Chronologie. </p>
*
* @return rule engine adapted for the type {@code java.time.YearMonth}
* @see #chronology()
*/
public static Chronology<YearMonth> threeten() {
return THREETEN;
}
@Override
protected Chronology<CalendarMonth> getChronology() {
return ENGINE;
}
@Override
protected CalendarMonth getContext() {
return this;
}
/**
* @serialData Uses <a href="../../../serialized-form.html#net.time4j.range.SPX">
* a dedicated serialization form</a> as proxy. The format
* is bit-compressed. The first byte contains in the six
* most significant bits the type-ID {@code 38}. Then the year number
* and the month number are written as int-primitives.
*
* Schematic algorithm:
*
* <pre>
* int header = 38;
* header <<= 2;
* out.writeByte(header);
* out.writeInt(getYear());
* out.writeInt(getMonthOfYear().getValue());
* </pre>
*
* @return replacement object in serialization graph
*/
private Object writeReplace() {
return new SPX(this, SPX.MONTH_TYPE);
}
/**
* @serialData Blocks because a serialization proxy is required.
* @param in object input stream
* @throws InvalidObjectException (always)
*/
private void readObject(ObjectInputStream in)
throws IOException {
throw new InvalidObjectException("Serialization proxy required.");
}
//~ Innere Klassen ----------------------------------------------------
private static class Merger
implements ChronoMerger<CalendarMonth> {
//~ Methoden ------------------------------------------------------
@Override
public CalendarMonth createFrom(
TimeSource<?> clock,
AttributeQuery attributes
) {
Timezone zone;
if (attributes.contains(Attributes.TIMEZONE_ID)) {
zone = Timezone.of(attributes.get(Attributes.TIMEZONE_ID));
} else if (attributes.get(Attributes.LENIENCY, Leniency.SMART).isLax()) {
zone = Timezone.ofSystem();
} else {
return null;
}
PlainDate date = Moment.from(clock.currentTime()).toZonalTimestamp(zone.getID()).toDate();
return CalendarMonth.of(date.getYear(), Month.valueOf(date.getMonth()));
}
@Override
public CalendarMonth createFrom(
ChronoEntity<?> entity,
AttributeQuery attributes,
boolean preparsing
) {
boolean lenient = attributes.get(Attributes.LENIENCY, Leniency.SMART).isLax();
return this.createFrom(entity, attributes, lenient, preparsing);
}
@Override
public CalendarMonth createFrom(
ChronoEntity<?> entity,
AttributeQuery attributes,
boolean lenient,
boolean preparsing
) {
int y = entity.getInt(YEAR);
if ((y >= GregorianMath.MIN_YEAR) && (y <= GregorianMath.MAX_YEAR)) {
int m = entity.getInt(PlainDate.MONTH_AS_NUMBER); // optimization
if ((m == Integer.MIN_VALUE) && entity.contains(MONTH_OF_YEAR)) {
m = entity.get(MONTH_OF_YEAR).getValue();
}
if (m != Integer.MIN_VALUE) {
return CalendarMonth.of(y, Month.valueOf(m));
}
} else if (y > Integer.MIN_VALUE) {
entity.with(ValidationElement.ERROR_MESSAGE, "Year out of bounds: " + y);
}
return null;
}
@Override
public String getFormatPattern(
DisplayStyle style,
Locale locale
) {
Map<String, String> map = CalendarText.getIsoInstance(locale).getTextForms();
String key = null;
switch (style.getStyleValue()) {
case DateFormat.FULL:
key = "F_yMMMM";
break;
case DateFormat.LONG:
key = "F_yMMM";
break;
case DateFormat.MEDIUM:
key = "F_yMM";
break;
case DateFormat.SHORT:
key = "F_yM";
break;
}
String pattern = getFormatPattern(map, key);
return ((pattern == null) ? "uuuu-MM" : pattern);
}
private static String getFormatPattern(
Map<String, String> map,
String key
) {
if (map.containsKey(key)) {
return map.get(key);
}
switch (key) {
case "F_yMMMM":
return getFormatPattern(map, "F_yMMM");
case "F_yMMM":
return getFormatPattern(map, "F_yMM");
case "F_yMM":
return getFormatPattern(map, "F_yM");
default:
return null;
}
}
@Override
public CalendarMonth createFrom(
TemporalAccessor threeten,
AttributeQuery attributes
) {
if (threeten.query(TemporalQueries.chronology()) == IsoChronology.INSTANCE) {
if (threeten.isSupported(ChronoField.YEAR)) {
if (threeten.isSupported(ChronoField.MONTH_OF_YEAR)) {
Leniency leniency = attributes.get(Attributes.LENIENCY, Leniency.SMART);
int year = threeten.get(ChronoField.YEAR);
int month = threeten.get(ChronoField.MONTH_OF_YEAR);
if (leniency.isLax()) {
PlainDate date = PlainDate.of(year, 1, 1);
date = date.with(PlainDate.MONTH_AS_NUMBER.setLenient(month));
return CalendarMonth.of(date.getYear(), date.getMonth());
} else {
return CalendarMonth.of(year, month);
}
}
}
}
return null;
}
}
private static class YearRule
implements IntElementRule<CalendarMonth> {
//~ Methoden ------------------------------------------------------
@Override
public Integer getValue(CalendarMonth context) {
return Integer.valueOf(context.year);
}
@Override
public Integer getMinimum(CalendarMonth context) {
return Integer.valueOf(GregorianMath.MIN_YEAR);
}
@Override
public Integer getMaximum(CalendarMonth context) {
return Integer.valueOf(GregorianMath.MAX_YEAR);
}
@Override
public boolean isValid(
CalendarMonth context,
Integer value
) {
if (value == null) {
return false;
}
int v = value.intValue();
return ((v >= GregorianMath.MIN_YEAR) && (v <= GregorianMath.MAX_YEAR));
}
@Override
public CalendarMonth withValue(
CalendarMonth context,
Integer value,
boolean lenient
) {
if (this.isValid(context, value)) {
return CalendarMonth.of(value.intValue(), context.month);
} else {
throw new IllegalArgumentException("Not valid: " + value);
}
}
@Override
public ChronoElement<?> getChildAtFloor(CalendarMonth context) {
return MONTH_OF_YEAR;
}
@Override
public ChronoElement<?> getChildAtCeiling(CalendarMonth context) {
return MONTH_OF_YEAR;
}
@Override
public int getInt(CalendarMonth context) {
return context.year;
}
@Override
public boolean isValid(
CalendarMonth context,
int value
) {
return ((value >= GregorianMath.MIN_YEAR) && (value <= GregorianMath.MAX_YEAR));
}
@Override
public CalendarMonth withValue(
CalendarMonth context,
int value,
boolean lenient
) {
if (this.isValid(context, value)) {
return CalendarMonth.of(value, context.month);
} else {
throw new IllegalArgumentException("Not valid: " + value);
}
}
}
private static class EnumMonthRule
implements ElementRule<CalendarMonth, Month> {
//~ Methoden ------------------------------------------------------
@Override
public Month getValue(CalendarMonth context) {
return context.month;
}
@Override
public Month getMinimum(CalendarMonth context) {
return Month.JANUARY;
}
@Override
public Month getMaximum(CalendarMonth context) {
return Month.DECEMBER;
}
@Override
public boolean isValid(
CalendarMonth context,
Month value
) {
return (value != null);
}
@Override
public CalendarMonth withValue(
CalendarMonth context,
Month value,
boolean lenient
) {
if (this.isValid(context, value)) {
return CalendarMonth.of(context.year, value);
} else {
throw new IllegalArgumentException("Not valid: " + value);
}
}
@Override
public ChronoElement<?> getChildAtFloor(CalendarMonth context) {
return null;
}
@Override
public ChronoElement<?> getChildAtCeiling(CalendarMonth context) {
return null;
}
}
private static class IntMonthRule
implements IntElementRule<CalendarMonth> {
//~ Methoden ------------------------------------------------------
@Override
public Integer getValue(CalendarMonth context) {
return Integer.valueOf(context.month.getValue());
}
@Override
public Integer getMinimum(CalendarMonth context) {
return Integer.valueOf(1);
}
@Override
public Integer getMaximum(CalendarMonth context) {
return Integer.valueOf(12);
}
@Override
public boolean isValid(
CalendarMonth context,
Integer value
) {
if (value == null) {
return false;
}
int v = value.intValue();
return ((v >= 1) && (v <= 12));
}
@Override
public CalendarMonth withValue(
CalendarMonth context,
Integer value,
boolean lenient
) {
if (this.isValid(context, value)) {
return CalendarMonth.of(context.year, value);
} else {
throw new IllegalArgumentException("Not valid: " + value);
}
}
@Override
public ChronoElement<?> getChildAtFloor(CalendarMonth context) {
return null;
}
@Override
public ChronoElement<?> getChildAtCeiling(CalendarMonth context) {
return null;
}
@Override
public int getInt(CalendarMonth context) {
return context.month.getValue();
}
@Override
public boolean isValid(
CalendarMonth context,
int value
) {
return ((value >= 1) && (value <= 12));
}
@Override
public CalendarMonth withValue(
CalendarMonth context,
int value,
boolean lenient
) {
if (this.isValid(context, value)) {
return CalendarMonth.of(context.year, value);
} else {
throw new IllegalArgumentException("Not valid: " + value);
}
}
}
private class Iter
implements Iterator<PlainDate> {
//~ Instanzvariablen ----------------------------------------------
private PlainDate current = CalendarMonth.this.start.getTemporal();
//~ Methoden ------------------------------------------------------
@Override
public boolean hasNext() {
return (this.current != null);
}
@Override
public PlainDate next() {
if (this.current == null) {
throw new NoSuchElementException();
} else {
PlainDate result = this.current;
PlainDate next = result.plus(1, CalendarUnit.DAYS);
this.current = ((next.getMonth() == result.getMonth()) ? next : null);
return result;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}