Java tutorial
/* * Copyright (C) 2015-2015 The Helenus Driver Project Authors. * * 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.github.helenusdriver.driver.impl; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.util.ArrayList; import java.util.Date; import java.util.EnumMap; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; import java.nio.ByteBuffer; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import org.apache.commons.lang3.LocaleUtils; import com.datastax.driver.core.Row; import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.exceptions.InvalidTypeException; import com.datastax.driver.core.utils.Bytes; /** * The <code>DataDecoder</code> abstract class defines the decoding functionality * for a column. * <p> * Supported conversions are: * - "ascii" to {@link Enum}, {@link Class}, {@link Locale}, {@link ZoneId}, or {@link String} * - "bigint" to {@link Long} * - "blob" to <code>byte[]</code> or {@link ByteBuffer} * - "boolean" to {@link Boolean} * - "counter" to {@link AtomicLong} or {@link Long} * - "decimal" to {@link BigDecimal} * - "double" to {@link Double} * - "float" to {@link Float} * - "inet" to {@link InetAddress} * - "int" to {@link Integer} * - "text" to {@link String} * - "timestamp" to {@link Date}, {@link Instant}, or {@link Long} * - "uuid" to {@link UUID} * - "varchar" to {@link String} * - "varint" to {@link BigInteger} * - "timeuuid" to {@link UUID} * * - "list<ctype>" to {@link List} of the corresponding element type * - "map<ctype, ctype>" to {@link Map} of the corresponding element types * - "set<ctype>" to {@link Set} of the corresponding element type * - "frozen<udt>" to an object of the user-defined type * * @copyright 2015-2015 The Helenus Driver Project Authors * * @author The Helenus Driver Project Authors * @version 1 - Jan 19, 2015 - paouelle - Creation * * @param <V> the type of data being decoded * * @since 1.0 */ public abstract class DataDecoder<V> { /** * Holds the "ascii" to {@link String} decoder. * * @author paouelle */ public final static DataDecoder<String> asciiToString = new DataDecoder<String>(String.class) { @Override protected String decodeImpl(Row row, String name, Class<String> clazz) { org.apache.commons.lang3.Validate.isTrue(String.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.getString(name); } @Override protected String decodeImpl(UDTValue uval, String name, Class<String> clazz) { org.apache.commons.lang3.Validate.isTrue(String.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.getString(name); } }; /** * Holds the "ascii" to {@link Enum} decoder. * * @author paouelle */ @SuppressWarnings("rawtypes") public final static DataDecoder<Enum> asciiToEnum = new DataDecoder<Enum>(Enum.class) { @SuppressWarnings("unchecked") @Override protected Enum decodeImpl(Row row, String name, Class<Enum> clazz) { org.apache.commons.lang3.Validate.isTrue(Enum.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String eval = row.getString(name); return (eval != null) ? Enum.valueOf(clazz, eval) : null; } @SuppressWarnings("unchecked") @Override protected Enum decodeImpl(UDTValue uval, String name, Class<Enum> clazz) { org.apache.commons.lang3.Validate.isTrue(Enum.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String eval = uval.getString(name); return (eval != null) ? Enum.valueOf(clazz, eval) : null; } }; /** * Holds the "ascii" to {@link Class} decoder. * * @author paouelle */ @SuppressWarnings("rawtypes") public final static DataDecoder<Class> asciiToClass = new DataDecoder<Class>(Class.class) { @Override protected Class decodeImpl(Row row, String name, Class<Class> clazz) { org.apache.commons.lang3.Validate.isTrue(Class.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String cname = row.getString(name); if (cname == null) { return null; } try { return Class.forName(cname); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } @Override protected Class decodeImpl(UDTValue uval, String name, Class<Class> clazz) { org.apache.commons.lang3.Validate.isTrue(Class.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String cname = uval.getString(name); if (cname == null) { return null; } try { return Class.forName(cname); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } }; /** * Holds the "ascii" to {@link Locale} decoder. * * @author paouelle */ public final static DataDecoder<Locale> asciiToLocale = new DataDecoder<Locale>(Locale.class) { @Override protected Locale decodeImpl(Row row, String name, Class<Locale> clazz) { org.apache.commons.lang3.Validate.isTrue(Locale.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String lname = row.getString(name); return (lname != null) ? LocaleUtils.toLocale(lname) : null; } @Override protected Locale decodeImpl(UDTValue uval, String name, Class<Locale> clazz) { org.apache.commons.lang3.Validate.isTrue(Locale.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String lname = uval.getString(name); return (lname != null) ? LocaleUtils.toLocale(lname) : null; } }; /** * Holds the "ascii" to {@link ZoneId} decoder. * * @author paouelle */ public final static DataDecoder<ZoneId> asciiToZoneId = new DataDecoder<ZoneId>(ZoneId.class) { @Override protected ZoneId decodeImpl(Row row, String name, Class<ZoneId> clazz) { org.apache.commons.lang3.Validate.isTrue(ZoneId.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String zid = row.getString(name); try { return (zid != null) ? ZoneId.of(zid) : null; } catch (DateTimeException e) { throw new IllegalArgumentException(e); } } @Override protected ZoneId decodeImpl(UDTValue uval, String name, Class<ZoneId> clazz) { org.apache.commons.lang3.Validate.isTrue(ZoneId.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final String zid = uval.getString(name); try { return (zid != null) ? ZoneId.of(zid) : null; } catch (DateTimeException e) { throw new IllegalArgumentException(e); } } }; /** * Holds the "bigint" to {@link Long} decoder. * * @author paouelle */ public final static DataDecoder<Long> bigintToLong = new DataDecoder<Long>(Long.class) { @Override protected Long decodeImpl(Row row, String name, Class<Long> clazz) { org.apache.commons.lang3.Validate.isTrue(Long.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : row.getLong(name); } @Override protected Long decodeImpl(UDTValue uval, String name, Class<Long> clazz) { org.apache.commons.lang3.Validate.isTrue(Long.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : uval.getLong(name); } }; /** * Holds the "blob" to <code>byte[]</code> decoder. * * @author paouelle */ public final static DataDecoder<byte[]> blobToByteArray = new DataDecoder<byte[]>(byte[].class) { @Override protected byte[] decodeImpl(Row row, String name, Class<byte[]> clazz) { org.apache.commons.lang3.Validate.isTrue(clazz.isArray(), "unsupported class '%s' to decode to", clazz.getName()); org.apache.commons.lang3.Validate.isTrue(Byte.TYPE == clazz.getComponentType(), "unsupported class '%s' to decode to", clazz.getName()); final ByteBuffer buf = row.getBytes(name); return (buf != null) ? Bytes.getArray(buf) : null; } @Override protected byte[] decodeImpl(UDTValue uval, String name, Class<byte[]> clazz) { org.apache.commons.lang3.Validate.isTrue(clazz.isArray(), "unsupported class '%s' to decode to", clazz.getName()); org.apache.commons.lang3.Validate.isTrue(Byte.TYPE == clazz.getComponentType(), "unsupported class '%s' to decode to", clazz.getName()); final ByteBuffer buf = uval.getBytes(name); return (buf != null) ? Bytes.getArray(buf) : null; } }; /** * Holds the "blob" to {@link ByteBuffer} decoder. * * @author paouelle */ public final static DataDecoder<ByteBuffer> blobToByteBuffer = new DataDecoder<ByteBuffer>(ByteBuffer.class) { @Override protected ByteBuffer decodeImpl(Row row, String name, Class<ByteBuffer> clazz) { org.apache.commons.lang3.Validate.isTrue(ByteBuffer.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getBytes(name); } @Override protected ByteBuffer decodeImpl(UDTValue uval, String name, Class<ByteBuffer> clazz) { org.apache.commons.lang3.Validate.isTrue(ByteBuffer.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getBytes(name); } }; /** * Holds the "boolean" to {@link Boolean} decoder. * * @author paouelle */ public final static DataDecoder<Boolean> booleanToBoolean = new DataDecoder<Boolean>(Boolean.class) { @Override protected Boolean decodeImpl(Row row, String name, Class<Boolean> clazz) { org.apache.commons.lang3.Validate.isTrue(Boolean.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : row.getBool(name); } @Override protected Boolean decodeImpl(UDTValue uval, String name, Class<Boolean> clazz) { org.apache.commons.lang3.Validate.isTrue(Boolean.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : uval.getBool(name); } }; /** * Holds the "counter" to {@link Long} decoder. * * @author paouelle */ public final static DataDecoder<Long> counterToLong = DataDecoder.bigintToLong; /** * Holds the "counter" to {@link AtomicLong} decoder. * * @author paouelle */ public final static DataDecoder<AtomicLong> counterToAtomicLong = new DataDecoder<AtomicLong>( AtomicLong.class) { @Override protected AtomicLong decodeImpl(Row row, String name, Class<AtomicLong> clazz) { org.apache.commons.lang3.Validate.isTrue(AtomicLong.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : new AtomicLong(row.getLong(name)); } @Override protected AtomicLong decodeImpl(UDTValue uval, String name, Class<AtomicLong> clazz) { org.apache.commons.lang3.Validate.isTrue(AtomicLong.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : new AtomicLong(uval.getLong(name)); } }; /** * Holds the "decimal" to {@link BigDecimal} decoder. * * @author paouelle */ public final static DataDecoder<BigDecimal> decimalToBigDecimal = new DataDecoder<BigDecimal>( BigDecimal.class) { @Override protected BigDecimal decodeImpl(Row row, String name, Class<BigDecimal> clazz) { org.apache.commons.lang3.Validate.isTrue(BigDecimal.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getDecimal(name); } @Override protected BigDecimal decodeImpl(UDTValue uval, String name, Class<BigDecimal> clazz) { org.apache.commons.lang3.Validate.isTrue(BigDecimal.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getDecimal(name); } }; /** * Holds the "double" to {@link Double} decoder. * * @author paouelle */ public final static DataDecoder<Double> doubleToDouble = new DataDecoder<Double>(Double.class) { @Override protected Double decodeImpl(Row row, String name, Class<Double> clazz) { org.apache.commons.lang3.Validate.isTrue(Double.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : row.getDouble(name); } @Override protected Double decodeImpl(UDTValue uval, String name, Class<Double> clazz) { org.apache.commons.lang3.Validate.isTrue(Double.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : uval.getDouble(name); } }; /** * Holds the "float" to {@link Float} decoder. * * @author paouelle */ public final static DataDecoder<Float> floatToFloat = new DataDecoder<Float>(Float.class) { @Override protected Float decodeImpl(Row row, String name, Class<Float> clazz) { org.apache.commons.lang3.Validate.isTrue(Float.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : row.getFloat(name); } @Override protected Float decodeImpl(UDTValue uval, String name, Class<Float> clazz) { org.apache.commons.lang3.Validate.isTrue(Float.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : uval.getFloat(name); } }; /** * Holds the "inet" to {@link InetAddress} decoder. * * @author paouelle */ public final static DataDecoder<InetAddress> inetToInetAddress = new DataDecoder<InetAddress>( InetAddress.class) { @Override protected InetAddress decodeImpl(Row row, String name, Class<InetAddress> clazz) { org.apache.commons.lang3.Validate.isTrue(InetAddress.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getInet(name); } @Override protected InetAddress decodeImpl(UDTValue uval, String name, Class<InetAddress> clazz) { org.apache.commons.lang3.Validate.isTrue(InetAddress.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getInet(name); } }; /** * Holds the "int" to {@link Integer} decoder. * * @author paouelle */ public final static DataDecoder<Integer> intToInteger = new DataDecoder<Integer>(Integer.class) { @Override protected Integer decodeImpl(Row row, String name, Class<Integer> clazz) { org.apache.commons.lang3.Validate.isTrue(Integer.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return row.isNull(name) ? null : row.getInt(name); } @Override protected Integer decodeImpl(UDTValue uval, String name, Class<Integer> clazz) { org.apache.commons.lang3.Validate.isTrue(Integer.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); return uval.isNull(name) ? null : uval.getInt(name); } }; /** * Holds the "text" to {@link String} decoder. * * @author paouelle */ public final static DataDecoder<String> textToString = DataDecoder.asciiToString; /** * Holds the "timestamp" to {@link Date} decoder. * * @author paouelle */ public final static DataDecoder<Date> timestampToDate = new DataDecoder<Date>(Date.class) { @Override protected Date decodeImpl(Row row, String name, Class<Date> clazz) { org.apache.commons.lang3.Validate.isTrue(Date.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getDate(name); } @Override protected Date decodeImpl(UDTValue uval, String name, Class<Date> clazz) { org.apache.commons.lang3.Validate.isTrue(Date.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getDate(name); } }; /** * Holds the "timestamp" to {@link Instant} decoder. * * @author paouelle */ public final static DataDecoder<Instant> timestampToInstant = new DataDecoder<Instant>(Instant.class) { @Override protected Instant decodeImpl(Row row, String name, Class<Instant> clazz) { org.apache.commons.lang3.Validate.isTrue(Instant.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final Date date = row.getDate(name); return (date != null) ? date.toInstant() : null; } @Override protected Instant decodeImpl(UDTValue uval, String name, Class<Instant> clazz) { org.apache.commons.lang3.Validate.isTrue(Instant.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); final Date date = uval.getDate(name); return (date != null) ? date.toInstant() : null; } }; /** * Holds the "timestamp" to {@link Long} decoder. * * @author paouelle */ public final static DataDecoder<Long> timestampToLong = new DataDecoder<Long>(Long.class) { @Override protected Long decodeImpl(Row row, String name, Class<Long> clazz) { org.apache.commons.lang3.Validate.isTrue(Long.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); final Date date = row.getDate(name); return (date != null) ? date.getTime() : null; } @Override protected Long decodeImpl(UDTValue uval, String name, Class<Long> clazz) { org.apache.commons.lang3.Validate.isTrue(Long.class == clazz, "unsupported class '%s' to decode to", clazz.getName()); final Date date = uval.getDate(name); return (date != null) ? date.getTime() : null; } }; /** * Holds the "uuid" to {@link UUID} decoder. * * @author paouelle */ public final static DataDecoder<UUID> uuidToUUID = new DataDecoder<UUID>(UUID.class) { @Override protected UUID decodeImpl(Row row, String name, Class<UUID> clazz) { org.apache.commons.lang3.Validate.isTrue(UUID.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getUUID(name); } @Override protected UUID decodeImpl(UDTValue uval, String name, Class<UUID> clazz) { org.apache.commons.lang3.Validate.isTrue(UUID.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getUUID(name); } }; /** * Holds the "varchar" to {@link String} decoder. * * @author paouelle */ public final static DataDecoder<String> varcharToString = DataDecoder.asciiToString; /** * Holds the "varint" to {@link BigInteger} decoder. * * @author paouelle */ public final static DataDecoder<BigInteger> varintToBigInteger = new DataDecoder<BigInteger>(BigInteger.class) { @Override protected BigInteger decodeImpl(Row row, String name, Class<BigInteger> clazz) { org.apache.commons.lang3.Validate.isTrue(BigInteger.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return row.getVarint(name); } @Override protected BigInteger decodeImpl(UDTValue uval, String name, Class<BigInteger> clazz) { org.apache.commons.lang3.Validate.isTrue(BigInteger.class.isAssignableFrom(clazz), "unsupported class '%s' to decode to", clazz.getName()); return uval.getVarint(name); } }; /** * Holds the "timeuuid" to {@link UUID} decoder. * * @author paouelle */ public final static DataDecoder<UUID> timeuuidToUUID = DataDecoder.uuidToUUID; /** * Gets a "list" to {@link List} decoder based on the given element class. * * @author paouelle * * @param eclazz the non-<code>null</code> class of elements * @param mandatory if the field associated with the decoder is mandatory or * represents a primary key * @return the non-<code>null</code> decoder for lists of the specified element * class */ @SuppressWarnings("rawtypes") public final static DataDecoder<List> list(final Class<?> eclazz, final boolean mandatory) { return new DataDecoder<List>(List.class) { @SuppressWarnings("unchecked") private List decodeImpl(Class<?> etype, List<Object> list) { if (list == null) { // safe to return as is unless mandatory, that is because Cassandra // returns null for empty lists and the schema definition requires // that mandatory and primary keys be non null if (mandatory) { return new ArrayList(16); } return list; } final List nlist = new ArrayList(list.size()); if (eclazz.isAssignableFrom(etype)) { // we only need to store elements to make sure list is modifiable nlist.addAll(list); } else { // will need to do some conversion of each element final ElementConverter converter = ElementConverter.getConverter(eclazz, etype); for (final Object o : list) { nlist.add((o != null) ? converter.convert(o) : null); } } return nlist; } @SuppressWarnings("unchecked") @Override protected List decodeImpl(Row row, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata row.getColumnDefinitions().getType(name).getTypeArguments().get(0).getName().asJavaClass(), row.isNull(name) ? null : row.getList(name, Object.class) // keeps things generic so we can handle our own errors ); } @SuppressWarnings("unchecked") @Override protected List decodeImpl(UDTValue uval, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata uval.getType().getFieldType(name).getTypeArguments().get(0).getName().asJavaClass(), uval.isNull(name) ? null : uval.getList(name, Object.class) // keeps things generic so we can handle our own errors ); } }; } /** * Gets a "set" to {@link Set} decoder based on the given element class. * * @author paouelle * * @param eclazz the non-<code>null</code> class of elements * @param mandatory if the field associated with the decoder is mandatory or * represents a primary key * @return the non-<code>null</code> decoder for sets of the specified element * class */ @SuppressWarnings("rawtypes") public final static DataDecoder<Set> set(final Class<?> eclazz, final boolean mandatory) { return new DataDecoder<Set>(Set.class) { @SuppressWarnings("unchecked") private Set decodeImpl(Class<?> etype, Set<Object> set) { if (set == null) { // safe to return as is unless mandatory, that is because Cassandra // returns null for empty sets and the schema definition requires // that mandatory and primary keys be non null if (mandatory) { if (eclazz.isEnum()) { // for enum values we create an enum set. Now we won't preserve the order // the entries were added but that should be fine anyways in this case return EnumSet.noneOf((Class<? extends Enum>) eclazz); } return new LinkedHashSet(16); // to keep order } return set; } final Set nset; if (eclazz.isEnum()) { // for enum values we create an enum set. Now we won't preserve the order // the entries were added but that should be fine anyways in this case nset = EnumSet.noneOf((Class<? extends Enum>) eclazz); } else { nset = new LinkedHashSet(set.size()); // to keep order } if (eclazz.isAssignableFrom(etype)) { // we only need to store elements to make sure list is modifiable nset.addAll(set); } else { // will need to do some conversion of each element final ElementConverter converter = ElementConverter.getConverter(eclazz, etype); for (final Object o : set) { nset.add((o != null) ? converter.convert(o) : null); } } return nset; } @SuppressWarnings("unchecked") @Override protected Set decodeImpl(Row row, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata row.getColumnDefinitions().getType(name).getTypeArguments().get(0).getName().asJavaClass(), row.isNull(name) ? null : row.getSet(name, Object.class) // keeps things generic so we can handle our own errors ); } @SuppressWarnings("unchecked") @Override protected Set decodeImpl(UDTValue uval, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata uval.getType().getFieldType(name).getTypeArguments().get(0).getName().asJavaClass(), uval.isNull(name) ? null : uval.getSet(name, Object.class) // keeps things generic so we can handle our own errors ); } }; } /** * Gets a "map" to {@link Map} decoder based on the given key and value classes. * * @author paouelle * * @param ekclazz the non-<code>null</code> class of keys * @param evclazz the non-<code>null</code> class of values * @param mandatory if the field associated with the decoder is mandatory or * represents a primary key * @return the non-<code>null</code> decoder for maps of the specified key and * value classes */ @SuppressWarnings("rawtypes") public final static DataDecoder<Map> map(final Class<?> ekclazz, final Class<?> evclazz, final boolean mandatory) { return new DataDecoder<Map>(Map.class) { @SuppressWarnings("unchecked") private Map decodeImpl(Class<?> ektype, Class<?> evtype, Map<Object, Object> map) { if (map == null) { // safe to return as is unless mandatory, that is because Cassandra // returns null for empty list and the schema definition requires // that mandatory and primary keys be non null if (mandatory) { if (ekclazz.isEnum()) { // for enum keys we create an enum map. Now we won't preserve the order // the entries were added but that should be fine anyways in this case return new EnumMap(ekclazz); } return new LinkedHashMap(16); // to keep order } return map; } final Map nmap; if (ekclazz.isEnum()) { // for enum keys we create an enum map. Now we won't preserve the order // the entries were added but that should be fine anyways in this case nmap = new EnumMap(ekclazz); } else { nmap = new LinkedHashMap(map.size()); // to keep order } if (ekclazz.isAssignableFrom(ektype) && evclazz.isAssignableFrom(evtype)) { nmap.putAll(map); } else { // will need to do some conversion of each element final ElementConverter kconverter = ElementConverter.getConverter(ekclazz, ektype); final ElementConverter vconverter = ElementConverter.getConverter(evclazz, evtype); for (final Map.Entry e : map.entrySet()) { final Object k = e.getKey(); final Object v = e.getValue(); nmap.put((k != null) ? kconverter.convert(k) : null, (v != null) ? vconverter.convert(v) : null); } } return nmap; } @SuppressWarnings("unchecked") @Override protected Map decodeImpl(Row row, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata row.getColumnDefinitions().getType(name).getTypeArguments().get(0).getName().asJavaClass(), row.getColumnDefinitions().getType(name).getTypeArguments().get(1).getName().asJavaClass(), row.isNull(name) ? null : row.getMap(name, Object.class, Object.class) // keeps things generic so we can handle our own errors ); } @SuppressWarnings("unchecked") @Override protected Map decodeImpl(UDTValue uval, String name, Class clazz) { return decodeImpl( // get the element type from the row's metadata uval.getType().getFieldType(name).getTypeArguments().get(0).getName().asJavaClass(), uval.getType().getFieldType(name).getTypeArguments().get(1).getName().asJavaClass(), uval.isNull(name) ? null : uval.getMap(name, Object.class, Object.class) // keeps things generic so we can handle our own errors ); } }; } /** * Gets a "udt" to {@link Object} decoder based on the given UDT class info. * * @author paouelle * * @param cinfo the user-defined class info * @return the non-<code>null</code> decoder for user-defined types represented * by the given class info */ @SuppressWarnings("rawtypes") public final static DataDecoder<Object> udt(final UDTClassInfoImpl<?> cinfo) { return new DataDecoder<Object>(Object.class) { @SuppressWarnings("unchecked") @Override protected Object decodeImpl(Row row, String name, Class clazz) { org.apache.commons.lang3.Validate.isTrue(clazz.isAssignableFrom(cinfo.getObjectClass()), "unsupported class '%s' to decode to", clazz.getName()); final UDTValue uval = row.getUDTValue(name); if (uval == null) { return null; } return cinfo.getObject(uval); } @Override protected Object decodeImpl(UDTValue uval, String name, Class clazz) { // not supported???? throw new IllegalArgumentException( "user-defined types based on other user-defined types are not supported"); } }; } /** * Holds the class this decoder decodes to. * * @author paouelle */ private final Class<V> clazz; /** * Instantiates a new <code>DataDecoder</code> object. * * @author paouelle * * @param clazz the non-<code>null</code> class this decoder decodes to */ DataDecoder(Class<V> clazz) { this.clazz = clazz; } /** * Decodes the specified column from the given row and return the value that * matches the decoder's defined class. * * @author paouelle * * @param row the non-<code>null</code> row where the column value is defined * @param name the non-<code>null</code> name of the column to decode * @param clazz the non-<code>null</code> class to decode to * @return the decoded object * @throws IllegalArgumentException if decoding to <code>clazz</code> is not * supported or if <code>name</code> is not part of the * result set this row is part of * @throws InvalidTypeException if column <code>name</code> type is not the * expected one */ protected abstract V decodeImpl(Row row, String name, Class<V> clazz); /** * Decodes the specified column from the given UDT value and return the value * that matches the decoder's defined class. * * @author paouelle * * @param uval the non-<code>null</code> UDT value where the column value is * defined * @param name the non-<code>null</code> name of the column to decode * @param clazz the non-<code>null</code> class to decode to * @return the decoded object * @throws IllegalArgumentException if decoding to <code>clazz</code> is not * supported or if <code>name</code> is not part of the * UDT value * @throws InvalidTypeException if column <code>name</code> type is not the * expected one */ protected abstract V decodeImpl(UDTValue uval, String name, Class<V> clazz); /** * Checks if this decoder can decode to the specified class. * * @author paouelle * * @param clazz the class to check if the decoder can decode to * @return <code>true</code> if this decoder can decode to the specified class; * <code>false</code> otherwise */ public boolean canDecodeTo(Class<?> clazz) { return this.clazz.isAssignableFrom(clazz); } /** * Decodes the specified column from the given row and return the value that * matches the decoder's defined class. * * @author paouelle * * @param row the non-<code>null</code> row where the column value is defined * @param name the non-<code>null</code> name of the column to decode * @param clazz the class to decode to * @return the decoded object or <code>null</code> if the specified column * doesn't exist in the rows * @throws NullPointerException if <code>row</code>, <code>name</code>, or * <code>clazz</code> is <code>null</code> * @throws IllegalArgumentException if decoding to <code>clazz</code> is not * supported or if <code>name</code> is not part of the * result set this row is part of * @throws InvalidTypeException if column <code>name</code> type is not the * expected one */ public V decode(Row row, String name, Class<V> clazz) { org.apache.commons.lang3.Validate.notNull(row, "invalid null row"); org.apache.commons.lang3.Validate.notNull(name, "invalid null name"); org.apache.commons.lang3.Validate.notNull(clazz, "invalid null clazz"); return decodeImpl(row, name, clazz); } /** * Decodes the specified column from the given UDT value and return the value * that matches the decoder's defined class. * * @author paouelle * * @param uval the non-<code>null</code> UDT value where the column value is * defined * @param name the non-<code>null</code> name of the column to decode * @param clazz the class to decode to * @return the decoded object or <code>null</code> if the specified column * doesn't exist in the UDT value * @throws NullPointerException if <code>uval</code>, <code>name</code>, or * <code>clazz</code> is <code>null</code> * @throws IllegalArgumentException if decoding to <code>clazz</code> is not * supported or if <code>name</code> is not part of the UDT value * @throws InvalidTypeException if column <code>name</code> type is not the * expected one */ public V decode(UDTValue uval, String name, Class<V> clazz) { org.apache.commons.lang3.Validate.notNull(uval, "invalid null UDT value"); org.apache.commons.lang3.Validate.notNull(name, "invalid null name"); org.apache.commons.lang3.Validate.notNull(clazz, "invalid null clazz"); return decodeImpl(uval, name, clazz); } } /** * The <code>ElementConverter</code> class defines a converting capability for * special supported combinations. * * @copyright 2015-2015 The Helenus Driver Project Authors * * @author The Helenus Driver Project Authors * @version 1 - Jan 19, 2015 - paouelle - Creation * * @since 1.0 */ abstract class ElementConverter { /** * Converts the specified row element. * * @author paouelle * * @param re the non-<code>null</code> row element to convert * @return the converted object */ public abstract Object convert(Object re); /** * Gets an element decoder that can be used to convert a Cassandra * returned data type into another data type based on special supported * combinations. * * @author paouelle * * @param eclass the non-<code>null</code> element class to decode to * @param reclass the non-<code>null</code> row's element class * @return a non-<code>null</code> element decoder for the provided combination * @throws IllegalArgumentException if the combination is not supported */ @SuppressWarnings("rawtypes") static ElementConverter getConverter(final Class eclass, Class reclass) { if (Enum.class.isAssignableFrom(eclass) && (String.class == reclass)) { return new ElementConverter() { @SuppressWarnings("unchecked") @Override public Object convert(Object re) { return Enum.valueOf(eclass, (String) re); } }; } else if (Locale.class.isAssignableFrom(eclass) && (String.class == reclass)) { return new ElementConverter() { @Override public Object convert(Object re) { return LocaleUtils.toLocale((String) re); } }; } else if (ZoneId.class.isAssignableFrom(eclass) && (String.class == reclass)) { return new ElementConverter() { @Override public Object convert(Object re) { try { return ZoneId.of((String) re); } catch (DateTimeException e) { throw new IllegalArgumentException(e); } } }; } else if (eclass.isArray() && (Byte.TYPE == eclass.getComponentType()) && ByteBuffer.class.isAssignableFrom(reclass)) { return new ElementConverter() { @Override public Object convert(Object re) { return Bytes.getArray((ByteBuffer) re); } }; } else if ((Long.class == eclass) && Date.class.isAssignableFrom(reclass)) { return new ElementConverter() { @Override public Object convert(Object re) { return ((Date) re).getTime(); } }; } else if ((Instant.class == eclass) && Date.class.isAssignableFrom(reclass)) { return new ElementConverter() { @Override public Object convert(Object re) { return ((Date) re).toInstant(); } }; } else if (eclass == reclass) { // special case for maps return new ElementConverter() { @Override public Object convert(Object re) { return re; } }; } throw new IllegalArgumentException( "unsupported element conversion from: " + reclass.getName() + " to: " + eclass.getName()); } }