Skip to content

Commit

Permalink
Fix autoinc casting to target column type
Browse files Browse the repository at this point in the history
  • Loading branch information
minborg committed Jun 9, 2015
1 parent a04f7a2 commit 20a2bdd
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 33 deletions.
Expand Up @@ -30,6 +30,8 @@
import com.speedment.core.db.impl.SqlFunction;
import com.speedment.core.platform.Platform;
import com.speedment.core.platform.component.DbmsHandlerComponent;
import com.speedment.core.runtime.typemapping.StandardJavaTypeMapping;
import com.speedment.util.LongUtil;
import static com.speedment.util.stream.OptionalUtil.unwrap;
import com.speedment.util.stream.builder.ReferenceStreamBuilder;
import com.speedment.util.stream.builder.pipeline.BasePipeline;
Expand Down Expand Up @@ -129,16 +131,17 @@ public Optional<ENTITY> persist(ENTITY entity, Consumer<MetaResult<ENTITY>> list

final List<Object> values = table.streamOf(Column.class).map(c -> unwrap(get(entity, c))).collect(Collectors.toList());

final Function<BUILDER, Consumer<List<Long>>> generatedKeyconsumer = b -> {
final Function<BUILDER, Consumer<List<Long>>> generatedKeyconsumer = builder -> {
return l -> {
if (!l.isEmpty()) {
final AtomicInteger cnt = new AtomicInteger();
// Just assume that they are in order, what else is there to do?
table.streamOf(Column.class)
.filter(Column::isAutoincrement)
.forEachOrdered(c -> {
System.out.println("COLUMN:" + c);
set(b, c, l.get(cnt.getAndIncrement()));
.forEachOrdered(column -> {
// Cast from Long to the column target type
final Object val = StandardJavaTypeMapping.parse(column.getMapping(), l.get(cnt.getAndIncrement()));
set(builder, column, val);
});
}
};
Expand Down
Expand Up @@ -18,6 +18,7 @@

import com.speedment.core.config.model.Dbms;
import com.speedment.core.exception.SpeedmentException;
import com.speedment.util.LongUtil;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
Expand All @@ -34,6 +35,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

/**
Expand All @@ -44,64 +46,80 @@ public enum StandardJavaTypeMapping implements JavaTypeMapping {

// If you add a mapping X here, make sure that AbstractSqlManager has a
// corresponding method getX(ResultSet, String)

OBJECT(Object.class, "Object", s -> (Object) s),
BOOLEAN(Boolean.class, "Boolean", Boolean::parseBoolean),
BYTE(Byte.class, "Byte", Byte::parseByte),
SHORT(Short.class, "Short", Short::parseShort),
INTEGER(Integer.class, "Int", Integer::parseInt),
LONG(Long.class, "Long", Long::parseLong),
FLOAT(Float.class, "Float", Float::parseFloat),
DOUBLE(Double.class, "Double", Double::parseDouble),
STRING(String.class, "String", Function.identity()),
DATE(Date.class, "Date", Date::valueOf),
TIME(Time.class, "Time", Time::valueOf),
TIMESTAMP(Timestamp.class, "Timestamp", Timestamp::valueOf),
BIG_DECIMAL(BigDecimal.class, "BigDecimal", s -> new BigDecimal(s)),
BLOB(Blob.class, "Blob", s -> unableToMap(Blob.class)),
CLOB(Clob.class, "Clob", s -> unableToMap(Clob.class)),
ARRAY(Array.class, "Array", s -> unableToMap(Array.class)),
REF(Ref.class, "Ref", s -> unableToMap(Ref.class)),
OBJECT(Object.class, "Object", s -> (Object) s, l -> (Object) l),
BOOLEAN(Boolean.class, "Boolean", Boolean::parseBoolean, l -> unableToMapLong(Boolean.class)),
BYTE(Byte.class, "Byte", Byte::parseByte, l -> LongUtil.cast(l, Byte.class)),
SHORT(Short.class, "Short", Short::parseShort, l -> LongUtil.cast(l, Short.class)),
INTEGER(Integer.class, "Int", Integer::parseInt, l -> LongUtil.cast(l, Integer.class)),
LONG(Long.class, "Long", Long::parseLong, Function.identity()),
FLOAT(Float.class, "Float", Float::parseFloat, l -> LongUtil.cast(l, Float.class)),
DOUBLE(Double.class, "Double", Double::parseDouble, l -> LongUtil.cast(l, Double.class)),
STRING(String.class, "String", Function.identity(), l -> Optional.ofNullable(l).map(lo -> lo.toString()).orElse(null)),
DATE(Date.class, "Date", Date::valueOf, l -> unableToMapLong(Date.class)),
TIME(Time.class, "Time", Time::valueOf, l -> unableToMapLong(Time.class)),
TIMESTAMP(Timestamp.class, "Timestamp", Timestamp::valueOf, l -> unableToMapLong(Timestamp.class)),
BIG_DECIMAL(BigDecimal.class, "BigDecimal", s -> new BigDecimal(s), l -> LongUtil.cast(l, BigDecimal.class)),
BLOB(Blob.class, "Blob", s -> unableToMapString(Blob.class), l -> unableToMapLong(Blob.class)),
CLOB(Clob.class, "Clob", s -> unableToMapString(Clob.class), l -> unableToMapLong(Clob.class)),
ARRAY(Array.class, "Array", s -> unableToMapString(Array.class), l -> unableToMapLong(Array.class)),
REF(Ref.class, "Ref", s -> unableToMapString(Ref.class), l -> unableToMapLong(Ref.class)),
URL(URL.class, "URL", s -> {
try {
return new URL(s);
} catch (MalformedURLException mfe) {
throw new SpeedmentException(mfe);
}
}),
ROW_ID(RowId.class, "RowId", s -> unableToMap(RowId.class)),
N_CLOB(NClob.class, "NClob", s -> unableToMap(NClob.class)),
SQLXML(SQLXML.class, "SQLXML", s -> unableToMap(SQLXML.class));
}, l -> unableToMapLong(URL.class)),
ROW_ID(RowId.class, "RowId", s -> unableToMapString(RowId.class), l -> unableToMapLong(RowId.class)),
N_CLOB(NClob.class, "NClob", s -> unableToMapString(NClob.class), l -> unableToMapLong(NClob.class)),
SQLXML(SQLXML.class, "SQLXML", s -> unableToMapString(SQLXML.class), l -> unableToMapLong(SQLXML.class));

private static final Map<Class<?>, Function<String, ?>> parsers = new HashMap<>();
private static final Map<Class<?>, Function<String, ?>> stringParsers = new HashMap<>();
private static final Map<Class<?>, Function<Long, ?>> longParsers = new HashMap<>();

static {
for (StandardJavaTypeMapping mapping : values()) {
parsers.put(mapping.clazz, mapping.stringMapper);
stringParsers.put(mapping.clazz, mapping.stringMapper);
longParsers.put(mapping.clazz, mapping.longMapper);
}
}

private static <T> T unableToMap(Class<T> clazz) {
throw new IllegalArgumentException("Unable to parse a string and make it " + clazz.toString());
private static <T> T unableToMapString(Class<T> clazz) {
return unableToMap(String.class, clazz);
}

private <T> StandardJavaTypeMapping(Class<T> clazz, String resultSetMethodName, Function<String, T> stringMapper) {
private static <T> T unableToMapLong(Class<T> clazz) {
return unableToMap(Long.class, clazz);
}

private static <T> T unableToMap(Class<?> from, Class<T> to) {
throw new IllegalArgumentException("Unable to parse a " + from.toString() + " and make it " + to.toString());
}

private <T> StandardJavaTypeMapping(Class<T> clazz, String resultSetMethodName, Function<String, T> stringMapper, Function<Long, T> longMapper) {
this.clazz = Objects.requireNonNull(clazz);
this.resultSetMethodName = Objects.requireNonNull(resultSetMethodName);
this.stringMapper = Objects.requireNonNull(stringMapper);
this.longMapper = longMapper;
}

private final Class<?> clazz;
private final String resultSetMethodName;
private final Function<String, ?> stringMapper;
private final Function<Long, ?> longMapper; // Used for auto-increment fields for example

// private <T> void put(Class<T> clazz, Function<String, T> mapper) {
// parsers.put(clazz, mapper);
// }

public static <T> T parse(Class<T> type, String inputValue) {
@SuppressWarnings("unchecked")
final Function<String, T> mapper = (Function<String, T>) parsers.getOrDefault(type, s -> unableToMap(type));
final Function<String, T> mapper = (Function<String, T>) stringParsers.getOrDefault(type, s -> unableToMapString(type));
return mapper.apply(inputValue);
}

public static <T> T parse(Class<T> type, Long inputValue) {
@SuppressWarnings("unchecked")
final Function<Long, T> mapper = (Function<Long, T>) longParsers.getOrDefault(type, s -> unableToMapLong(type));
return mapper.apply(inputValue);
}

Expand Down
75 changes: 75 additions & 0 deletions src/main/java/com/speedment/util/LongUtil.java
@@ -0,0 +1,75 @@
/**
*
* Copyright (c) 2006-2015, Speedment, Inc. All Rights Reserved.
*
* 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.speedment.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
*
* @author pemi
*/
public class LongUtil {

private LongUtil() {
}

@SuppressWarnings("unchecked")
public static <T extends Number> T cast(Long l, Class<T> targetClass) {
if (l == null) {
return null;
}
if (targetClass == Byte.class) {
checkRange(l, Byte.MIN_VALUE, Byte.MAX_VALUE);
return (T) (Byte) l.byteValue();
} else if (targetClass == Short.class) {
checkRange(l, Short.MIN_VALUE, Short.MAX_VALUE);
return (T) (Short) l.shortValue();
} else if (targetClass == Integer.class) {
checkRange(l, Integer.MIN_VALUE, Integer.MAX_VALUE);
return (T) (Integer) l.intValue();
} else if (targetClass == Long.class) {
return (T) l;
} else if (targetClass == Float.class) {
return (T) (Float) l.floatValue();
} else if (targetClass == Double.class) {
return (T) (Double) l.doubleValue();
} else if (targetClass == BigInteger.class) {
return (T) BigInteger.valueOf(l);
} else if (targetClass == BigDecimal.class) {
return (T) BigDecimal.valueOf(l);
} else if (targetClass == AtomicInteger.class) {
checkRange(l, Integer.MIN_VALUE, Integer.MAX_VALUE);
return (T) new AtomicInteger(l.intValue());
} else if (targetClass == AtomicLong.class) {
return (T) new AtomicLong(l);
}
throw new IllegalArgumentException("Unable to cast Long to " + targetClass);
}

public static void checkRange(Long value, long min, long max) {
if (value == null) {
return; // No check
}
if (value > max || value < min) {
throw new IllegalArgumentException("The value " + value + " is out of range [" + min + ", " + max + "]");
}
}

}

0 comments on commit 20a2bdd

Please sign in to comment.