/*******************************************************************************
* Copyright 2013 SAP AG
*
* Licensed 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.sap.core.odata.core.edm;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sap.core.odata.api.edm.EdmFacets;
import com.sap.core.odata.api.edm.EdmLiteralKind;
import com.sap.core.odata.api.edm.EdmSimpleTypeException;
/**
* <p>Implementation of the EDM simple type Time.</p>
* <p>Arguably, this type is intended to represent a time of day, not an instance in time.
* The time value is interpreted and formatted as local time.</p>
* <p>Formatting simply ignores the year, month, and day parts of time instances.
* Parsing returns a Calendar object where all unused fields have been cleared.</p>
* @author SAP AG
*/
public class EdmTime extends AbstractSimpleType {
private static final Pattern PATTERN = Pattern.compile(
"PT(?:(\\p{Digit}{1,2})H)?(?:(\\p{Digit}{1,4})M)?(?:(\\p{Digit}{1,5})(?:\\.(\\p{Digit}+?)0*)?S)?");
private static final EdmTime instance = new EdmTime();
public static EdmTime getInstance() {
return instance;
}
@Override
public Class<?> getDefaultType() {
return Calendar.class;
}
@Override
protected <T> T internalValueOfString(final String value, final EdmLiteralKind literalKind, final EdmFacets facets, final Class<T> returnType) throws EdmSimpleTypeException {
Calendar valueCalendar;
if (literalKind == EdmLiteralKind.URI) {
if (value.length() > 6 && value.startsWith("time'") && value.endsWith("'")) {
valueCalendar = parseLiteral(value.substring(5, value.length() - 1), facets);
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value));
}
} else {
valueCalendar = parseLiteral(value, facets);
}
if (returnType.isAssignableFrom(Calendar.class)) {
return returnType.cast(valueCalendar);
} else if (returnType.isAssignableFrom(Long.class)) {
return returnType.cast(valueCalendar.getTimeInMillis());
} else if (returnType.isAssignableFrom(Date.class)) {
return returnType.cast(valueCalendar.getTime());
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(returnType));
}
}
private Calendar parseLiteral(final String literal, final EdmFacets facets) throws EdmSimpleTypeException {
final Matcher matcher = PATTERN.matcher(literal);
if (!matcher.matches()
|| (matcher.group(1) == null && matcher.group(2) == null && matcher.group(3) == null)) {
throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(literal));
}
Calendar dateTimeValue = Calendar.getInstance();
dateTimeValue.clear();
dateTimeValue.set(Calendar.HOUR_OF_DAY,
matcher.group(1) == null ? 0 : Integer.parseInt(matcher.group(1)));
dateTimeValue.set(Calendar.MINUTE,
matcher.group(2) == null ? 0 : Integer.parseInt(matcher.group(2)));
dateTimeValue.set(Calendar.SECOND,
matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3)));
if (matcher.group(4) != null) {
if (facets == null || facets.getPrecision() == null || facets.getPrecision() >= matcher.group(4).length()) {
if (matcher.group(4).length() <= 3) {
dateTimeValue.set(Calendar.MILLISECOND,
Short.parseShort(matcher.group(4) + "000".substring(0, 3 - matcher.group(4).length())));
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(literal));
}
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_FACETS_NOT_MATCHED.addContent(literal, facets));
}
}
if (dateTimeValue.get(Calendar.DAY_OF_YEAR) == 1) {
return dateTimeValue;
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(literal));
}
}
@Override
protected <T> String internalValueToString(final T value, final EdmLiteralKind literalKind, final EdmFacets facets) throws EdmSimpleTypeException {
Calendar dateTimeValue;
if (value instanceof Date) {
dateTimeValue = Calendar.getInstance();
dateTimeValue.clear();
dateTimeValue.setTime((Date) value);
} else if (value instanceof Calendar) {
dateTimeValue = (Calendar) ((Calendar) value).clone();
} else if (value instanceof Long) {
dateTimeValue = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
dateTimeValue.clear();
dateTimeValue.setTimeInMillis((Long) value);
} else {
throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(value.getClass()));
}
StringBuilder result = new StringBuilder(15); // 15 characters are enough for millisecond precision.
result.append('P');
result.append('T');
result.append(dateTimeValue.get(Calendar.HOUR_OF_DAY));
result.append('H');
result.append(dateTimeValue.get(Calendar.MINUTE));
result.append('M');
result.append(dateTimeValue.get(Calendar.SECOND));
try {
EdmDateTime.appendMilliseconds(result, dateTimeValue.get(Calendar.MILLISECOND), facets);
} catch (final IllegalArgumentException e) {
throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_FACETS_NOT_MATCHED.addContent(value, facets), e);
}
result.append('S');
return result.toString();
}
@Override
public String toUriLiteral(final String literal) {
return "time'" + literal + "'";
}
}