blob: 2a2cbeeb9b21aa5025c3fbeb1406ae871d4703e4 [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.jackrabbit.oak.plugins.value;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.TimeZone;
import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import org.apache.jackrabbit.oak.api.Blob;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.memory.StringBasedBlob;
import org.apache.jackrabbit.util.ISO8601;
/**
* Utility class defining the conversion that take place between {@link org.apache.jackrabbit.oak.api.PropertyState}s
* of different types. All conversions defined in this class are compatible with the conversions specified
* in JSR-283 $3.6.4. However, some conversion in this class might not be defined in JSR-283.
* <p>
* Example:
* <pre>
* double three = convert("3.0").toDouble();
* </pre>
*/
public final class Conversions {
private static final TimeZone UTC = TimeZone.getTimeZone("GMT+00:00");
private Conversions() {}
/**
* A converter converts a value to its representation as a specific target type. Not all target
* types might be supported for a given value in which case implementations throw an exception.
* The default implementations of the various conversion methods all operate on the string
* representation of the underlying value (i.e. call {@code Converter.toString()}.
*/
public abstract static class Converter {
/**
* Convert to string
* @return string representation of the converted value
*/
public abstract String toString();
/**
* Convert to binary. This default implementation returns an new instance
* of {@link StringBasedBlob}.
* @return binary representation of the converted value
*/
public Blob toBinary() {
return new StringBasedBlob(toString());
}
/**
* Convert to long. This default implementation is based on {@code Long.parseLong(String)}.
* @return long representation of the converted value
* @throws NumberFormatException
*/
public long toLong() {
return Long.parseLong(toString());
}
/**
* Convert to double. This default implementation is based on {@code Double.parseDouble(String)}.
* @return double representation of the converted value
* @throws NumberFormatException
*/
public double toDouble() {
return Double.parseDouble(toString());
}
/**
* Convert to date. This default implementation is based on {@code ISO8601.parse(String)}.
* @return date representation of the converted value
* @throws IllegalArgumentException if the string cannot be parsed into a date
*/
public Calendar toCalendar() {
Calendar date = ISO8601.parse(toString());
if (date == null) {
throw new IllegalArgumentException("Not a date string: " + toString());
}
return date;
}
/**
* Convert to date. This default implementation delegates to {@link #toCalendar()}
* and returns the {@code ISO8601.format(Calendar)} value of the calendar.
* @return date representation of the converted value
* @throws IllegalArgumentException if the string cannot be parsed into a date
*/
public String toDate() {
return ISO8601.format(toCalendar());
}
/**
* Convert to boolean. This default implementation is based on {@code Boolean.parseBoolean(String)}.
* @return boolean representation of the converted value
*/
public boolean toBoolean() {
return Boolean.parseBoolean(toString());
}
/**
* Convert to decimal. This default implementation is based on {@code new BigDecimal(String)}.
* @return decimal representation of the converted value
* @throws NumberFormatException
*/
public BigDecimal toDecimal() {
return new BigDecimal(toString());
}
}
/**
* Create a converter for a string.
* @param value The string to convert
* @return A converter for {@code value}
* @throws NumberFormatException
*/
public static Converter convert(final String value) {
return new Converter() {
@Override
public String toString() {
return value;
}
};
}
/**
* Create a converter for a binary.
* For the conversion to {@code String} the binary in interpreted as UTF-8 encoded string.
* @param value The binary to convert
* @return A converter for {@code value}
* @throws IllegalArgumentException if the binary is inaccessible
*/
public static Converter convert(final Blob value) {
return new Converter() {
@Override
public String toString() {
try {
InputStream in = value.getNewStream();
try {
return new String(ByteStreams.toByteArray(in), Charsets.UTF_8);
}
finally {
in.close();
}
}
catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public Blob toBinary() {
return value;
}
};
}
/**
* Create a converter for a long. {@code String.valueOf(long)} is used for the conversion to {@code String}.
* The conversions to {@code double} and {@code long} return the {@code value} itself.
* The conversion to decimal uses {@code new BigDecimal.valueOf(long)}.
* The conversion to date interprets the value as number of milliseconds since {@code 1970-01-01T00:00:00.000Z}.
* @param value The long to convert
* @return A converter for {@code value}
*/
public static Converter convert(final long value) {
return new Converter() {
@Override
public String toString() {
return String.valueOf(value);
}
@Override
public long toLong() {
return value;
}
@Override
public double toDouble() {
return value;
}
@Override
public Calendar toCalendar() {
Calendar date = Calendar.getInstance(UTC);
date.setTimeInMillis(value);
return date;
}
@Override
public BigDecimal toDecimal() {
return BigDecimal.valueOf(value);
}
};
}
/**
* Create a converter for a double. {@code String.valueOf(double)} is used for the conversion to {@code String}.
* The conversions to {@code double} and {@code long} return the {@code value} itself where in the former case
* the value is casted to {@code long}.
* The conversion to decimal uses {@code BigDecimal.valueOf(double)}.
* The conversion to date interprets {@code toLong()} as number of milliseconds since
* {@code 1970-01-01T00:00:00.000Z}.
* @param value The double to convert
* @return A converter for {@code value}
*/
public static Converter convert(final double value) {
return new Converter() {
@Override
public String toString() {
return String.valueOf(value);
}
@Override
public long toLong() {
return (long) value;
}
@Override
public double toDouble() {
return value;
}
@Override
public Calendar toCalendar() {
Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
date.setTimeInMillis(toLong());
return date;
}
@Override
public BigDecimal toDecimal() {
return BigDecimal.valueOf(value);
}
};
}
/**
* Create a converter for a date. {@code ISO8601.format(Calendar)} is used for the conversion to {@code String}.
* The conversions to {@code double}, {@code long} and {@code BigDecimal} return the number of milliseconds
* since {@code 1970-01-01T00:00:00.000Z}.
* @param value The date to convert
* @return A converter for {@code value}
*/
public static Converter convert(final String value, Type<?> type) {
if (type == Type.DECIMAL) {
return convert(convert(value).toDecimal());
} else if (type == Type.DOUBLE) {
return convert(convert(value).toDouble());
} else if (type == Type.LONG) {
return convert(convert(value).toLong());
} else if (type != Type.DATE) {
return convert(value);
} else {
return new Converter() {
@Override
public String toString() {
return value;
}
@Override
public Calendar toCalendar() {
return ISO8601.parse(toString());
}
@Override
public long toLong() {
return toCalendar().getTimeInMillis();
}
@Override
public double toDouble() {
return toLong();
}
@Override
public BigDecimal toDecimal() {
return new BigDecimal(toLong());
}
};
}
}
/**
* Create a converter for a boolean. {@code Boolean.toString(boolean)} is used for the conversion to {@code String}.
* @param value The boolean to convert
* @return A converter for {@code value}
*/
public static Converter convert(final boolean value) {
return new Converter() {
@Override
public String toString() {
return Boolean.toString(value);
}
@Override
public boolean toBoolean() {
return value;
}
};
}
/**
* Create a converter for a decimal. {@code BigDecimal.toString()} is used for the conversion to {@code String}.
* {@code BigDecimal.longValue()} and {@code BigDecimal.doubleValue()} is used for the conversions to
* {@code long} and {@code double}, respectively.
* The conversion to date interprets {@code toLong()} as number of milliseconds since
* {@code 1970-01-01T00:00:00.000Z}.
* @param value The decimal to convert
* @return A converter for {@code value}
*/
public static Converter convert(final BigDecimal value) {
return new Converter() {
@Override
public String toString() {
return value.toString();
}
@Override
public long toLong() {
return value.longValue();
}
@Override
public double toDouble() {
return value.doubleValue();
}
@Override
public Calendar toCalendar() {
Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
date.setTimeInMillis(toLong());
return date;
}
@Override
public BigDecimal toDecimal() {
return value;
}
};
}
}