/*
* Copyright (c) 2013 Evolveum
*
* 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.evolveum.midpoint.prism.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.util.Date;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.time.DateUtils;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.jetbrains.annotations.Nullable;
/**
* Generic universal type converter. It is supposed to covert anything to anything as long
* as there is any way to convert it. This means converting string containing a decimal representation
* of a number to int, PolyString to string, etc. This is supposed to work in a fashion similar to
* many scripting languages (e.g. perl) where the programmer does not really care about the type
* and the type conversion is done automatically.
*
* @author Radovan Semancik
*/
public class JavaTypeConverter {
final static String[] DATE_FORMATS = {
"yyyy-MM-dd'T'HH:mm:ss.SSSX",
"yyyy-MM-dd'T'HH:mm:ssz",
"EEE, dd MMM yyyy HH:mm:ss z",
"EEE, dd MMM yyyy HH:mm zzzz",
"EEE MMM dd HH:mm:ss z yyyy",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz",
"yyyy-MM-dd'T'HH:mm:sszzzz",
"yyyy-MM-dd'T'HH:mm:ss z",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HHmmss.SSSz",
"yyyy-MM-dd"
};
@SuppressWarnings("unchecked")
public static <T> T convert(Class<T> expectedType, Object rawValue) {
return (T) convert(expectedType, rawValue, true);
}
public static Object convert(Class<?> expectedType, Object rawValue, boolean failIfImpossible) {
if (rawValue == null || expectedType.isInstance(rawValue)) {
return rawValue;
}
if (rawValue instanceof PrismPropertyValue<?>) {
rawValue = ((PrismPropertyValue<?>)rawValue).getValue();
}
// This really needs to be checked twice
if (rawValue == null || expectedType.isInstance(rawValue)) {
return rawValue;
}
// Primitive types
// boolean
if (expectedType == boolean.class && rawValue instanceof Boolean) {
return ((Boolean)rawValue);
}
if (expectedType == Boolean.class && rawValue instanceof String) {
return (Boolean)Boolean.parseBoolean((((String)rawValue)).trim());
}
if (expectedType == Boolean.class && rawValue instanceof PolyString) {
return (Boolean)Boolean.parseBoolean((rawValue.toString().trim()));
}
if (expectedType == boolean.class && rawValue instanceof String) {
return (Boolean)Boolean.parseBoolean(((String)rawValue).trim());
}
if (expectedType == boolean.class && rawValue instanceof PolyString) {
return (Boolean)Boolean.parseBoolean((rawValue).toString().trim());
}
if (expectedType == String.class && rawValue instanceof Boolean) {
return rawValue.toString();
}
// int
if (expectedType == int.class && rawValue instanceof Integer) {
return ((Integer)rawValue);
}
if (expectedType == Integer.class && rawValue instanceof String) {
return (Integer)Integer.parseInt(((String)rawValue).trim());
}
if (expectedType == int.class && rawValue instanceof String) {
return (Integer)Integer.parseInt(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof Integer) {
return rawValue.toString();
}
if (expectedType == int.class && rawValue instanceof Long) {
return (Integer)((Long)rawValue).intValue();
}
if (expectedType == long.class && rawValue instanceof Long) {
return (rawValue);
}
if (expectedType == Long.class && rawValue instanceof String) {
return (Long)Long.parseLong(((String)rawValue).trim());
}
if (expectedType == long.class && rawValue instanceof String) {
return (Long)Long.parseLong(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof Long) {
return rawValue.toString();
}
if (expectedType == float.class && rawValue instanceof Float) {
return ((Float)rawValue);
}
if (expectedType == Float.class && rawValue instanceof String) {
return (Float)Float.parseFloat(((String)rawValue).trim());
}
if (expectedType == float.class && rawValue instanceof String) {
return (Float)Float.parseFloat(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof Float) {
return rawValue.toString();
}
if (expectedType == double.class && rawValue instanceof Double) {
return ((Double)rawValue);
}
if (expectedType == Double.class && rawValue instanceof String) {
return (Double)Double.parseDouble(((String)rawValue).trim());
}
if (expectedType == double.class && rawValue instanceof String) {
return (Double)Double.parseDouble(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof Float) {
return rawValue.toString();
}
if (expectedType == byte.class && rawValue instanceof Byte) {
return ((Byte)rawValue);
}
if (expectedType == Byte.class && rawValue instanceof String) {
return (Byte)Byte.parseByte(((String)rawValue));
}
if (expectedType == byte.class && rawValue instanceof String) {
return (Byte)Byte.parseByte(((String)rawValue));
}
if (expectedType == String.class && rawValue instanceof Byte) {
return rawValue.toString();
}
if (expectedType == BigInteger.class && rawValue instanceof String) {
return new BigInteger(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof BigInteger) {
return rawValue.toString().trim();
}
if (expectedType == BigDecimal.class && rawValue instanceof String) {
return new BigDecimal(((String)rawValue).trim());
}
if (expectedType == String.class && rawValue instanceof BigDecimal) {
return ((BigDecimal)rawValue).toString().trim();
}
if (expectedType == PolyString.class && rawValue instanceof String) {
return new PolyString((String)rawValue);
}
if (expectedType == PolyStringType.class && rawValue instanceof String) {
PolyStringType polyStringType = new PolyStringType();
polyStringType.setOrig((String)rawValue);
return polyStringType;
}
if (expectedType == String.class && rawValue instanceof PolyString) {
return ((PolyString)rawValue).getOrig();
}
if (expectedType == String.class && rawValue instanceof PolyStringType) {
return ((PolyStringType)rawValue).getOrig();
}
if (expectedType == PolyString.class && rawValue instanceof PolyStringType) {
return ((PolyStringType)rawValue).toPolyString();
}
if (expectedType == PolyString.class && rawValue instanceof Integer) {
return new PolyString(((Integer)rawValue).toString());
}
if (expectedType == PolyStringType.class && rawValue instanceof PolyString) {
PolyStringType polyStringType = new PolyStringType((PolyString)rawValue);
return polyStringType;
}
if (expectedType == PolyStringType.class && rawValue instanceof Integer) {
PolyStringType polyStringType = new PolyStringType(((Integer)rawValue).toString());
return polyStringType;
}
// Date and time
if (expectedType == XMLGregorianCalendar.class && rawValue instanceof Long) {
XMLGregorianCalendar xmlCalType = XmlTypeConverter.createXMLGregorianCalendar((Long)rawValue);
return xmlCalType;
}
if (expectedType == XMLGregorianCalendar.class && rawValue instanceof String) {
XMLGregorianCalendar xmlCalType = magicDateTimeParse((String)rawValue);
return xmlCalType;
}
if (expectedType == String.class && rawValue instanceof XMLGregorianCalendar) {
return ((XMLGregorianCalendar)rawValue).toXMLFormat();
}
if (expectedType == Long.class && rawValue instanceof XMLGregorianCalendar) {
return (Long) XmlTypeConverter.toMillis((XMLGregorianCalendar)rawValue);
}
// XML Enums (JAXB)
if (expectedType.isEnum() && expectedType.getAnnotation(XmlEnum.class) != null && rawValue instanceof String) {
return XmlTypeConverter.toXmlEnum(expectedType, ((String)rawValue).trim());
}
if (expectedType == String.class && rawValue.getClass().isEnum() && rawValue.getClass().getAnnotation(XmlEnum.class) != null) {
return XmlTypeConverter.fromXmlEnum(rawValue);
}
// Java Enums
if (expectedType.isEnum() && rawValue instanceof String) {
return Enum.valueOf((Class<Enum>)expectedType, ((String)rawValue).trim());
}
if (expectedType == String.class && rawValue.getClass().isEnum()) {
return rawValue.toString();
}
//QName
if (expectedType == QName.class && rawValue instanceof QName){
return rawValue;
}
if (expectedType == QName.class && rawValue instanceof String){
return QNameUtil.uriToQName(((String)rawValue).trim());
}
if (failIfImpossible) {
throw new IllegalArgumentException("Expected " + expectedType + " type, but got " + rawValue.getClass());
} else {
return rawValue;
}
}
private static XMLGregorianCalendar magicDateTimeParse(String stringDate) {
try {
return XmlTypeConverter.createXMLGregorianCalendar(stringDate);
} catch (IllegalArgumentException e) {
// No XML format. This is still quite OK. It we will try other formats ...
}
Date date;
try {
date = DateUtils.parseDate(stringDate, DATE_FORMATS);
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
return XmlTypeConverter.createXMLGregorianCalendar(date);
}
public static <T> boolean isTypeCompliant(@Nullable T value, @Nullable Class<?> expectedClass) {
if (value == null || expectedClass == null) {
return true;
}
Class<?> wrapped = ClassUtils.primitiveToWrapper(expectedClass);
return wrapped.isAssignableFrom(((Object) value).getClass()); // auto-boxing of primitive types
// TODO PolyString vs String - should be treated here?
// TODO int vs long vs ...
}
}