001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014
015package org.basepom.mojo.propertyhelper.definitions;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018
019import org.basepom.mojo.propertyhelper.FieldContext;
020import org.basepom.mojo.propertyhelper.ValueCache;
021import org.basepom.mojo.propertyhelper.ValueProvider;
022import org.basepom.mojo.propertyhelper.fields.DateField;
023
024import java.io.IOException;
025import java.time.Instant;
026import java.time.ZoneId;
027import java.time.ZonedDateTime;
028import java.time.format.DateTimeFormatter;
029import java.time.format.DateTimeParseException;
030import java.util.Optional;
031import java.util.StringJoiner;
032import java.util.function.Function;
033
034import com.google.common.annotations.VisibleForTesting;
035
036public class DateDefinition extends FieldDefinition<ZonedDateTime> {
037
038    /**
039     * Timezone for this date. Field injected by Maven.
040     */
041    String timezone = null;
042
043    /**
044     * Value for this date. Field injected by Maven.
045     */
046    Long value = null;
047
048    public DateDefinition() {
049    }
050
051    @VisibleForTesting
052    DateDefinition(String id) {
053        super(id);
054    }
055
056    public ZoneId getTimezone() {
057        return Optional.ofNullable(timezone)
058            .map(ZoneId::of)
059            .orElse(ZoneId.systemDefault());
060    }
061
062    public Optional<Long> getValue() {
063        return Optional.ofNullable(value);
064    }
065
066    @Override
067    public DateField createField(FieldContext context, ValueCache valueCache) throws IOException {
068        checkNotNull(context, "context is null");
069        checkNotNull(valueCache, "valueCache is null");
070
071        check();
072
073        final ValueProvider dateValue = valueCache.getValueProvider(this);
074        return new DateField(this, dateValue, context);
075    }
076
077    @Override
078    public Function<ZonedDateTime, String> getPreFormat() {
079        DateTimeFormatter dateTimeFormatter = getFormatter().orElseGet(this::getFallbackFormatter);
080        return v -> v == null ? "" : dateTimeFormatter.format(v);
081    }
082
083    @Override
084    public Function<String, String> getPostFormat() {
085        return Function.identity();
086    }
087
088    public Function<String, ZonedDateTime> getParser() {
089        return value -> getFormatter().map(f -> {
090            try {
091                return ZonedDateTime.parse(value, f);
092            } catch (DateTimeParseException e) {
093                return null;
094            }
095        }).orElseGet(() -> parseLong(value).map(getLongParser())
096            .orElseGet(() -> ZonedDateTime.parse(value, getFallbackFormatter())));
097    }
098
099    private Optional<Long> parseLong(String value) {
100        try {
101            return Optional.of(Long.parseLong(value));
102        } catch (NumberFormatException e) {
103            return Optional.empty();
104        }
105    }
106
107    public Function<Long, ZonedDateTime> getLongParser() {
108        return longValue -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(longValue), getTimezone());
109    }
110
111    public Optional<DateTimeFormatter> getFormatter() {
112        return Optional.ofNullable(format)
113            .map(DateTimeFormatter::ofPattern)
114            .map(formatter -> formatter.withZone(getTimezone()));
115    }
116
117    public DateTimeFormatter getFallbackFormatter() {
118        return DateTimeFormatter.ISO_DATE_TIME.withZone(getTimezone());
119    }
120
121    @Override
122    public String toString() {
123        return new StringJoiner(", ", DateDefinition.class.getSimpleName() + "[", "]")
124            .add("timezone='" + timezone + "'")
125            .add("value=" + value)
126            .add(super.toString())
127            .toString();
128    }
129}