/*
* Licensed to STRATIO (C) under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. The STRATIO (C) licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.stratio.cassandra.lucene.util;
import com.stratio.cassandra.lucene.IndexException;
import org.apache.cassandra.utils.UUIDGen;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* Unified class for parse a {@link Date}s from {@link Object}s including a {@code String} pattern.
*
* @author Eduardo Alonso {@literal <eduardoalonso@stratio.com>}
*/
public class DateParser {
/** The default date pattern for {@code String}s. */
public static final String DEFAULT_PATTERN = "yyyy/MM/dd HH:mm:ss.SSS Z";
/** The pattern value for timestamps. */
public static final String TIMESTAMP_PATTERN_FIELD = "timestamp";
private static final Long DAYS_TO_MILLIS = 24L * 60L * 60L * 1000L;
/** The {@link SimpleDateFormat} pattern. */
private final String pattern;
/** The thread safe date format. */
private final ThreadLocal<DateFormat> concurrentDateFormat;
/**
* Constructor with pattern.
*
* @param pattern the {@link SimpleDateFormat} pattern to use
*/
public DateParser(String pattern) {
this.pattern = pattern == null ? DEFAULT_PATTERN : pattern;
// Validate pattern if is not "timestamp"
if (!this.pattern.equals(TIMESTAMP_PATTERN_FIELD)) {
new SimpleDateFormat(this.pattern);
this.concurrentDateFormat = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(DateParser.this.pattern);
}
};
this.concurrentDateFormat.get().setLenient(false);
} else {
this.concurrentDateFormat = null;
}
}
/**
* Returns the {@link Date} represented by the specified {@link Object}, or {@code null} if the specified {@link
* Object} is {@code null}.
*
* @param value the {@link Object} to be parsed
* @return the parsed {@link Date}
*/
public Date parse(Object value) {
if (value == null) {
return null;
} else if (value instanceof Date) {
return (Date) value;
} else if (value instanceof Integer) {
if ((Integer) value < 0) {
throw new IndexException("Required positive Integer for dates but found '%s'", value);
} else {
return new Date(DAYS_TO_MILLIS * (Integer) value);
}
} else if (value instanceof Long) {
if ((Long) value < 0L) {
throw new IndexException("Required positive Long for dates but found '%s'", value);
} else {
return new Date((Long) value);
}
} else if (value instanceof UUID) {
try {
return new Date(UUIDGen.unixTimestamp((UUID) value));
} catch (UnsupportedOperationException e) {
throw new IndexException("Required a version 1 UUID but found '%s'", value);
}
} else {
if (pattern.equals(TIMESTAMP_PATTERN_FIELD)) {
return parseAsTimestamp(value);
} else {
return parseAsFormattedDate(value);
}
}
}
private Date parseAsTimestamp(Object value) {
Long valueLong;
if (value instanceof Number) {
valueLong = ((Number) value).longValue();
} else {
try {
valueLong = Long.parseLong((value).toString());
} catch (NumberFormatException e) {
valueLong = null;
}
}
if (valueLong != null) {
return new Date(valueLong);
} else {
throw new IndexException("Valid timestamp required but found '%s'", value);
}
}
private Date parseAsFormattedDate(Object value) {
try {
return concurrentDateFormat.get().parse(value.toString());
} catch (ParseException e) {
throw new IndexException("Required date with pattern '%s' but found '%s'", pattern, value);
}
}
/** {@inheritDoc} */
@Override
public String toString() {
return pattern;
}
/**
* Returns the {@link String} representation of the specified {@link Date}.
*
* @param date the date
* @return the {@link String} representation of {@code Date}
*/
public String toString(Date date) {
if (pattern.equals(TIMESTAMP_PATTERN_FIELD)) {
return ((Long) date.getTime()).toString();
} else {
return concurrentDateFormat.get().format(date);
}
}
}