/* * 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.stanbol.entityhub.core.mapping; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; import org.apache.stanbol.entityhub.core.model.InMemoryValueFactory; import org.apache.stanbol.entityhub.core.utils.TimeUtils; import org.apache.stanbol.entityhub.servicesapi.defaults.DataTypeEnum; import org.apache.stanbol.entityhub.servicesapi.model.Reference; import org.apache.stanbol.entityhub.servicesapi.model.Text; import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory; /** * This class is used to convert values to a specific dataType. By default the * Factory comes initialised with converters for all dataTypes defined in the * {@link DataTypeEnum}.<p> * When the default configuration is sufficient, than one should use the * static {@link #getInstance()} methods. When one needs to change the configuration * it is advised to create an own instance by using the * {@link #ValueConverterFactory(ValueFactory)}.<p> * Calling {@link #registerConverter(ValueConverter)} on an instance created by * the static {@link #getInstance()} methods will result in an * {@link IllegalStateException}. * * @author Rupert Westenthaler * */ public class ValueConverterFactory { private static ValueConverterFactory defaultInstance; /** * Getter for the ValueConverterFactory instance using the default configurations * of converters. This configuration can not be changed.<p> * If you need to use a specific configuration use the public constructor * to create your own private instance! * @return the default ValueConverterFactory instance */ public static ValueConverterFactory getDefaultInstance(){ if(defaultInstance == null){ defaultInstance = new ValueConverterFactory(true); } return defaultInstance; } private boolean readonly = false; /** * Creates a new factory instance that supports conversions for all * datatypes defines in {@link DataTypeEnum}.<p> * Please note the static {@link #getInstance(ValueFactory)} methods that * should be used instead if one do not plan to change the configuration of * the created instance. * @param valueFactory the {@link ValueFactory} instance to be used to * create {@link Text} and {@link Reference} instances. If <code>null</code> * the {@link InMemoryValueFactory} is used. */ public ValueConverterFactory(){ this(false); } /** * Internally used to ensure readonly state for instances created by the * static {@link #getInstance(ValueFactory)} methods. * @see #ValueConverterFactory(ValueFactory) */ private ValueConverterFactory(boolean readonly){ init(); this.readonly = readonly; } /** * Populates the factory with the default configuration that supports all * {@link DataTypeEnum} entries. */ private void init(){ registerConverter(new AnyUriConverter()); registerConverter(new BooleanConverter()); registerConverter(new ByteConverter()); registerConverter(new DateConverter()); registerConverter(new DateTimeConverter()); registerConverter(new DecimalConverter()); registerConverter(new DoubleConverter()); registerConverter(new DurationConverter()); registerConverter(new FloatConverter()); registerConverter(new IntConverter()); registerConverter(new IntegerConverter()); registerConverter(new LongConverter()); registerConverter(new ReferenceConverter()); registerConverter(new ShortConverter()); registerConverter(new StringConverter()); registerConverter(new TextConverter()); registerConverter(new TimeConverter()); } Map<String,ValueConverter<?>> uri2converter = new HashMap<String, ValueConverter<?>>(); // Map<Class<?>,ValueConverter<?>> type2converter = new HashMap<Class<?>, ValueConverter<?>>(); /** * Registers a converter for the {@link ValueConverter#getDataType()}. If * a converter for this datatype is already present, than it is replaced by * this one. */ protected void registerConverter(ValueConverter<?> converter){ if(readonly){ throw new IllegalStateException("Unable to register an converter for a read only ValueConverter Factory!" + "Do not use the static getInstance(..) Methods if you need to change the configuration."); } uri2converter.put(converter.getDataType(), converter); } // public <T> ValueConverter<T> getConverter(Class<T> javaType){ // return null; // } /** * Getter for the converter of the parsed datatype uri. * @param the uri of the datatype. For datatypes registered in the * {@link DataTypeEnum} the {@link DataTypeEnum#getUri()} should be used. * @return the converter or <code>null</code> if no converter is present for * the parsed datatype uri. */ public ValueConverter<?> getConverter(String dataTypeUri){ return uri2converter.get(dataTypeUri); } /** * Converts the parsed value to the specified dataType. * @param value the value to convert. <code>null</code> is parsed to the * converter and may be supported for some datatypes. If not supported, * than parsing <code>null</code> results in <code>null</code> to be * returned. * @param dataTypeUri the URI of the dataType * @return the converted value or <code>null</code> if no conversion was * possible. * @throws IllegalArgumentException if the parsed dataTyeUri is <code>null</code> */ public Object convert(Object value,String dataTypeUri,ValueFactory valueFactory) throws IllegalArgumentException { if(dataTypeUri == null){ throw new IllegalArgumentException("The parsed datatype URI MUST NOT be NULL!"); } ValueConverter<?> converter = uri2converter.get(dataTypeUri); return converter != null?converter.convert(value,valueFactory):null; } /*-------------------------------------------------------------------------- * Implementation of the ValueConverters for the dataTypes defined by * DataTypeEnum * ------------------------------------------------------------------------- */ /** * This Interface defines an simple converter interface that allows a * registry to get metadata about the type the converter can create and * second the {@link #convert(Object)} method that is called to convert * to the target type. * @author Rupert Westenthaler * * @param <T> the type of created objects */ public static interface ValueConverter<T> { /** * The URI of the dataType created by this converter * @return the data type */ String getDataType(); /** * Converts the Value or returns <code>null</code> if the conversion was not * possible. * @param value the value to convert. <code>null</code> is parsed to the * converter and may be supported for some datatypes. If not supported, * than parsing <code>null</code> results in <code>null</code> to be * returned. * @param valueFactory the ValueFactory used to create the converted value * @return the converted value or <code>null</code> if the conversion was not * possible */ T convert(Object value,ValueFactory valueFactory); } public static class BooleanConverter implements ValueConverter<Boolean>{ @Override public Boolean convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Boolean){ return (Boolean)value; } else { //can not use the Boolean.parse method, because I need to return //null for strings != true || false ... String strValue = value.toString(); return "true".equalsIgnoreCase(strValue)? Boolean.TRUE:"false".equalsIgnoreCase(strValue)? Boolean.FALSE:null; } } @Override public String getDataType() {return DataTypeEnum.Boolean.getUri();} } public static class ByteConverter implements ValueConverter<Byte>{ @Override public Byte convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Byte){ return (Byte)value; } else if(value instanceof Long || value instanceof Integer || value instanceof Short){ long longValue = ((Long)value).longValue(); if(longValue <= Byte.MAX_VALUE && longValue >= Byte.MIN_VALUE){ return (byte)longValue; } else { return null; } } else if(value instanceof Float || value instanceof Double){ try { return BigDecimal.valueOf(((Number)value).doubleValue()).byteValueExact(); } catch (ArithmeticException e) { return null; } } else { try { return new BigDecimal(value.toString()).byteValueExact(); } catch (NumberFormatException e){ return null; } catch (ArithmeticException e) { return null; } } } @Override public String getDataType() {return DataTypeEnum.Byte.getUri();} } public static class ShortConverter implements ValueConverter<Short>{ @Override public Short convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Short){ return (Short)value; } else if(value instanceof Long || value instanceof Integer){ long longValue = ((Long)value).longValue(); if(longValue <= Short.MAX_VALUE && longValue >= Short.MIN_VALUE){ return (short)longValue; } else { return null; } } else if(value instanceof Float || value instanceof Double){ try { return BigDecimal.valueOf(((Number)value).doubleValue()).shortValueExact(); } catch (ArithmeticException e) { return null; } } else { try { return new BigDecimal(value.toString()).shortValueExact(); } catch (NumberFormatException e){ return null; } catch (ArithmeticException e) { return null; } } } @Override public String getDataType() {return DataTypeEnum.Short.getUri();} } public static class IntConverter implements ValueConverter<Integer>{ @Override public Integer convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Integer){ return (Integer)value; } else if(value instanceof Long){ long longValue = ((Long)value).longValue(); if(longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE){ return (int)longValue; } else { return null; } } else if(value instanceof Float || value instanceof Double){ try { return BigDecimal.valueOf(((Number)value).doubleValue()).intValueExact(); } catch (ArithmeticException e) { return null; } } else { try { return new BigDecimal(value.toString()).intValueExact(); } catch (NumberFormatException e){ return null; } catch (ArithmeticException e) { return null; } } } @Override public String getDataType() {return DataTypeEnum.Int.getUri();} } public static class LongConverter implements ValueConverter<Long>{ @Override public Long convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Long){ return (Long)value; } else if(value instanceof Integer){ return ((Integer)value).longValue(); } else if(value instanceof Float || value instanceof Double){ try { return BigDecimal.valueOf(((Number)value).doubleValue()).longValueExact(); } catch (ArithmeticException e) { return null; } } else { try { return new BigDecimal(value.toString()).longValueExact(); } catch (NumberFormatException e){ return null; } catch (ArithmeticException e) { return null; } } } @Override public String getDataType() {return DataTypeEnum.Long.getUri();} } public static class FloatConverter implements ValueConverter<Float>{ @Override public Float convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Float){ return (Float)value; } else { try { return Float.parseFloat(value.toString()); } catch (NumberFormatException e){ return null;} } } @Override public String getDataType() {return DataTypeEnum.Float.getUri();} } public static class DoubleConverter implements ValueConverter<Double>{ @Override public Double convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Double){ return (Double)value; } else { try { return Double.parseDouble(value.toString()); } catch (NumberFormatException e){ return null;} } } @Override public String getDataType() {return DataTypeEnum.Double.getUri();} } public static class IntegerConverter implements ValueConverter<BigInteger>{ @Override public BigInteger convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof BigInteger){ return (BigInteger)value; } else { try { //would also support 10000000000000000000000000000000.0 return new BigDecimal(value.toString()).toBigIntegerExact(); } catch (NumberFormatException e){ return null; } catch (ArithmeticException e) { return null; } } } @Override public String getDataType() {return DataTypeEnum.Integer.getUri();} } public static class DecimalConverter implements ValueConverter<BigDecimal>{ @Override public BigDecimal convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof BigDecimal){ return (BigDecimal)value; } else { try { return new BigDecimal(value.toString()); } catch (NumberFormatException e){ return null;} } } @Override public String getDataType() {return DataTypeEnum.Decimal.getUri();} } public static class AnyUriConverter implements ValueConverter<Reference>{ @Override public Reference convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Reference){ return (Reference)value; } else if(value instanceof URI || value instanceof URL){ return valueFactory.createReference(value); } else { try { //For converting we only accept absolute URIs if(new URI(value.toString()).isAbsolute()){; return valueFactory.createReference(value); } else { return null; } } catch (URISyntaxException e){ return null;} } } @Override public String getDataType() {return DataTypeEnum.AnyUri.getUri();} } public static class ReferenceConverter extends AnyUriConverter { //same as AnyUri just parse Reference as DataType @Override public String getDataType() {return DataTypeEnum.Reference.getUri();} } public static class DateTimeConverter implements ValueConverter<Date>{ private final DataTypeEnum dataType; public DateTimeConverter(){ this.dataType = DataTypeEnum.DateTime; } protected DateTimeConverter(DataTypeEnum dataType){ if(dataType == null){ throw new IllegalArgumentException("Parsed DataType MUST NOT be NULL"); } this.dataType = dataType; } @Override public Date convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Date){ return (Date)value; } else if(value instanceof XMLGregorianCalendar){ return ((XMLGregorianCalendar)value).toGregorianCalendar().getTime(); } else if(value instanceof Calendar){ return ((Calendar)value).getTime(); } else { try { return TimeUtils.toDate(dataType, value); } catch (RuntimeException e){ return null;} } } @Override public String getDataType() {return dataType.getUri();} } public static class DateConverter extends DateTimeConverter { public DateConverter(){ super(DataTypeEnum.Date); } } public static class TimeConverter extends DateTimeConverter { public TimeConverter(){ super(DataTypeEnum.Time); } } /** * Converts String values to {@link Text} (without an language tag. Does * NOT convert any other values such as {@link Number}s or {@link Reference}s * @author Rupert Westenthaler * */ public static class TextConverter implements ValueConverter<Text> { @Override public Text convert(Object value, ValueFactory valueFactory) { if (value == null) { return null; } if(value instanceof Text){ return (Text)value; } else if(value instanceof String){ return valueFactory.createText(value); } else { //do not convert other values return null; } } @Override public String getDataType() { return DataTypeEnum.Text.getUri(); } } /** * Converts any value to {@link String} by using the {@link #toString()} * method of the parsed value * @author Rupert Westenthaler * */ public static class StringConverter implements ValueConverter<String> { @Override public String convert(Object value, ValueFactory valueFactory) { return value.toString(); } @Override public String getDataType() { return DataTypeEnum.String.getUri(); } } public static class DurationConverter implements ValueConverter<Duration> { private boolean nullAsZeroLengthDuration; /** * Creates a converter for durations */ public DurationConverter(){ this(false); } /** * Creates a converter for durations * @param nullAsZeroLengthDuration if true, than null values parsed to the * {@link #convert(Object)} are interpreted as durations with zero length. */ public DurationConverter(boolean nullAsZeroLengthDuration){ this.nullAsZeroLengthDuration = nullAsZeroLengthDuration; } @Override public Duration convert(Object value, ValueFactory valueFactory) { if(value == null){ if(nullAsZeroLengthDuration){ return TimeUtils.toDuration(null,true); } else { return null; } } else if(value instanceof Duration){ return (Duration)value; } else { try{ return TimeUtils.toDuration(value); } catch (RuntimeException e){ return null; } } } /** * Getter for the state if a null value should be interpreted as a * duration with zero length. * @return the state */ public final boolean isNullAsZeroLengthDuration() { return nullAsZeroLengthDuration; } /** * Setter for the state if a null value should be interpreted as a * duration with zero length. * @param nullAsZeroLengthDuration the new state */ public final void setNullAsZeroLengthDuration(boolean nullAsZeroLengthDuration) { this.nullAsZeroLengthDuration = nullAsZeroLengthDuration; } @Override public String getDataType() { return DataTypeEnum.Duration.getUri(); } } }