blob: 3d6a9738121dbeb6595a66b723fb7ddaa324783d [file] [log] [blame]
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF 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 org.apache.sling.contentparser.api;
import java.lang.reflect.Array;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.osgi.annotation.versioning.ConsumerType;
@ConsumerType
public final class ParserHelper {
private static final String ECMA_DATE_FORMAT = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z";
private static final String ISO_8601_MILLISECONDS_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSVV";
private static final Locale DATE_FORMAT_LOCALE = Locale.US;
private static final DateTimeFormatter ECMA_DATE_FORMATTER = DateTimeFormatter.ofPattern(ECMA_DATE_FORMAT, DATE_FORMAT_LOCALE);
private static final DateTimeFormatter ISO_8601_MILLISECONDS_DATE_FORMATTER =
DateTimeFormatter.ofPattern(ISO_8601_MILLISECONDS_DATE_FORMAT,
DATE_FORMAT_LOCALE);
/**
* Attempts to parse a {@code string} using first the {@link #ISO_8601_MILLISECONDS_DATE_FORMAT} format and then the {@link
* #ECMA_DATE_FORMAT}.
*
* @param string the string to parse
* @return a {@link Calendar} containing the parsed date or {@code null}, if the parsing failed
*/
public static Calendar parseDate(String string) {
try {
return parseDate(string, ISO_8601_MILLISECONDS_DATE_FORMATTER);
} catch (DateTimeParseException e) {
try {
return parseDate(string, ECMA_DATE_FORMATTER);
} catch (DateTimeParseException ee) {
return null;
}
}
}
/**
* Converts a multi-value property to a single object.
*
* @param values the multi-value property's values
* @return an object representation of the multi-value property
* @throws ParseException when the provided {@code values} array contains null values, {@link Map} values, or the values are not of the
* same type
*/
public static Object convertSingleTypeArray(Object[] values) {
if (values.length == 0) {
return values;
}
Class<?> itemType = null;
for (Object value : values) {
if (value == null) {
throw new ParseException("Multi-value array must not contain null values.");
}
if (value instanceof Map) {
throw new ParseException("Multi-value array must not contain maps/objects.");
}
if (itemType == null) {
itemType = value.getClass();
} else if (itemType != value.getClass()) {
throw new ParseException("Multi-value array must not contain values with different types "
+ "(" + itemType.getName() + ", " + value.getClass().getName() + ").");
}
}
Object convertedArray = Array.newInstance(itemType, values.length);
for (int i = 0; i < values.length; i++) {
Array.set(convertedArray, i, values[i]);
}
return convertedArray;
}
private static Calendar parseDate(String string, DateTimeFormatter formatter) throws DateTimeParseException {
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(string, formatter);
final Instant instant = zonedDateTime.toInstant();
Calendar calendar = Calendar.getInstance();
calendar.setTime(Date.from(instant));
calendar.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getOffset()));
return calendar;
}
}