package com.brightgenerous.orm.mapper;
import static com.brightgenerous.commons.ObjectUtils.*;
import static com.brightgenerous.commons.StringUtils.*;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.brightgenerous.bean.AbstractBean;
import com.brightgenerous.bean.BeanUtils;
import com.brightgenerous.commons.StringUtils;
import com.brightgenerous.datasource.DataSource;
import com.brightgenerous.lang.Args;
import com.brightgenerous.orm.Field;
import com.brightgenerous.orm.Fields;
import com.brightgenerous.orm.IField;
import com.brightgenerous.orm.IValue;
import com.brightgenerous.orm.Sort;
import com.brightgenerous.orm.Sorts;
import com.brightgenerous.orm.mapper.TableMapper.Flag;
/*
* Helper Class for MyBatis Mapper.xml script.
*
* may be, not necessary to you...
*/
public abstract class MapperMethods {
static class InstanceKey implements Serializable {
private static final long serialVersionUID = 8248992966756884872L;
private final Class<? extends MapperMethods> clazz;
private final String table;
public InstanceKey(Class<? extends MapperMethods> clazz, String table) {
this.clazz = clazz;
this.table = table;
}
@Override
public int hashCode() {
final int multiplier = 37;
int result = 17;
result = (multiplier * result) + hashCodeEscapeNull(clazz);
result = (multiplier * result) + hashCodeEscapeNull(table);
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof InstanceKey)) {
return false;
}
InstanceKey other = (InstanceKey) obj;
if (!equalsEscapeNull(clazz, other.clazz)) {
return false;
}
if (!equalsEscapeNull(table, other.table)) {
return false;
}
return true;
}
}
private static volatile Class<? extends MapperMethods> mapperMethodClazz;
private static final Map<InstanceKey, SoftReference<? extends MapperMethods>> cache = new ConcurrentHashMap<>();
protected MapperMethods() {
}
public static void set(Class<? extends MapperMethods> clazz) {
Args.notNull(clazz, "clazz");
if (mapperMethodClazz != clazz) {
synchronized (DataSource.class) {
if (mapperMethodClazz != clazz) {
checkAndGetConstructor(clazz);
mapperMethodClazz = clazz;
}
}
}
}
public static MapperMethods get() {
return get((String) null);
}
public static MapperMethods get(String table) {
Class<? extends MapperMethods> clazz = mapperMethodClazz;
if (clazz == null) {
synchronized (MapperMethods.class) {
clazz = mapperMethodClazz;
if (clazz == null) {
throw new IllegalStateException(
"the Mapper Methods Class yet has not been set.");
}
}
}
return get(clazz, table);
}
public static MapperMethods get(Class<? extends MapperMethods> clazz) {
return get(clazz, null);
}
public static MapperMethods get(Class<? extends MapperMethods> clazz, String table) {
if (clazz == null) {
return get(table);
}
InstanceKey key = new InstanceKey(clazz, table);
MapperMethods ret = null;
{
SoftReference<? extends MapperMethods> ref = cache.get(key);
if (ref != null) {
ret = ref.get();
}
}
if (ret == null) {
synchronized (cache) {
SoftReference<? extends MapperMethods> ref = cache.get(key);
if (ref != null) {
ret = ref.get();
}
if (ret == null) {
cache.remove(key);
Set<InstanceKey> dels = new HashSet<>();
for (Entry<InstanceKey, SoftReference<? extends MapperMethods>> entry : cache
.entrySet()) {
if (entry.getValue().get() == null) {
dels.add(entry.getKey());
}
}
if (!dels.isEmpty()) {
for (InstanceKey del : dels) {
cache.remove(del);
}
}
ret = newInstance(clazz, table);
cache.put(key, new SoftReference<>(ret));
}
}
}
return ret;
}
private static MapperMethods newInstance(Class<? extends MapperMethods> clazz, String table) {
try {
Constructor<? extends MapperMethods> cons = checkAndGetConstructor(clazz);
MapperMethods ret = cons.newInstance();
ret.setTable(table);
return ret;
} catch (SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static Constructor<? extends MapperMethods> checkAndGetConstructor(
Class<? extends MapperMethods> clazz) {
try {
Constructor<? extends MapperMethods> cons = clazz.getDeclaredConstructor();
if (!Modifier.isPublic(cons.getModifiers()) && !cons.isAccessible()) {
cons.setAccessible(true);
}
return cons;
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
throw new RuntimeException(e);
}
}
private String table;
protected String getTable() {
return table;
}
protected void setTable(String table) {
this.table = table;
}
public boolean isNull(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof AbstractBean)) {
return false;
}
return BeanUtils.primaryNull((AbstractBean) obj);
}
public boolean notNull(Object obj) {
return !isNull(obj);
}
public boolean contains(String[] propertys) {
return contains(propertys, Collections.EMPTY_SET);
}
public boolean contains(String[] propertys, String include) {
return contains(propertys, asList(include));
}
public boolean contains(String[] propertys, String include, String[] excludes) {
return contains(propertys, asList(include), excludes);
}
public boolean contains(String[] propertys, String include, Collection<String> excludes) {
return contains(propertys, asList(include), excludes);
}
public boolean contains(String[] propertys, String[] includes) {
return contains(propertys, asList(includes));
}
public boolean contains(String[] propertys, String[] includes, String[] excludes) {
return contains(propertys, asList(includes), excludes);
}
public boolean contains(String[] propertys, String[] includes, Collection<String> excludes) {
return contains(propertys, asList(includes), excludes);
}
public boolean contains(String[] propertys, Collection<String> includes) {
return contains(propertys, includes, Collections.EMPTY_SET);
}
public boolean contains(String[] propertys, Collection<String> includes, String[] excludes) {
return contains(propertys, includes, asList(excludes));
}
public boolean contains(String[] propertys, Collection<String> includes,
Collection<String> excludes) {
return contains(asList(propertys), includes, excludes);
}
public boolean contains(Collection<String> propertys) {
return contains(propertys, Collections.EMPTY_SET);
}
public boolean contains(Collection<String> propertys, String include) {
return contains(propertys, asList(include));
}
public boolean contains(Collection<String> propertys, String include, String[] excludes) {
return contains(propertys, asList(include), excludes);
}
public boolean contains(Collection<String> propertys, String include,
Collection<String> excludes) {
return contains(propertys, asList(include), excludes);
}
public boolean contains(Collection<String> propertys, String[] includes) {
return contains(propertys, asList(includes));
}
public boolean contains(Collection<String> propertys, String[] includes, String[] excludes) {
return contains(propertys, asList(includes), excludes);
}
public boolean contains(Collection<String> propertys, String[] includes,
Collection<String> excludes) {
return contains(propertys, asList(includes), excludes);
}
public boolean contains(Collection<String> propertys, Collection<String> includes) {
return contains(propertys, includes, Collections.EMPTY_SET);
}
public boolean contains(Collection<String> propertys, Collection<String> includes,
String[] excludes) {
return contains(propertys, includes, asList(excludes));
}
public boolean contains(Collection<String> propertys, Collection<String> includes,
Collection<String> excludes) {
if ((propertys == null) || propertys.isEmpty() || (includes == null) || includes.isEmpty()) {
return false;
}
List<String> retains = removeAll(propertys, excludes);
for (String include : includes) {
if (retains.contains(include)) {
return true;
}
}
return false;
}
public LinkedHashMap<String, String> filterSelects() {
return filterSelects(Collections.EMPTY_SET);
}
public LinkedHashMap<String, String> filterSelects(String[] propertys) {
return filterSelects(asList(propertys));
}
public LinkedHashMap<String, String> filterSelects(String[] propertys, String[] excludes) {
return filterSelects(propertys, asList(excludes));
}
public LinkedHashMap<String, String> filterSelects(String[] propertys,
Collection<String> excludes) {
return filterSelects(asList(propertys), excludes);
}
public LinkedHashMap<String, String> filterSelects(Collection<String> propertys) {
return filterSelects(propertys, Collections.EMPTY_SET);
}
public LinkedHashMap<String, String> filterSelects(Collection<String> propertys,
String[] excludes) {
return filterSelects(propertys, asList(excludes));
}
public LinkedHashMap<String, String> filterSelects(Collection<String> propertys,
Collection<String> excludes) {
LinkedHashMap<String, String> ret = new LinkedHashMap<>();
{
List<TableMapper> tms = getTableMappers(getTable(), removeAll(propertys, excludes));
if ((tms != null) && !tms.isEmpty()) {
for (TableMapper tm : tms) {
if (tm == null) {
continue;
}
Map<String, String> pffcs = tm.getPropertyFieldFullColumns(Flag.SELECT);
if ((pffcs != null) && !pffcs.isEmpty()) {
for (Entry<String, String> e : pffcs.entrySet()) {
String k = e.getKey();
if (!ret.containsKey(k)) {
ret.put(k, e.getValue());
}
}
}
}
}
}
return ret;
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields) {
return filterFields(fields, Collections.EMPTY_SET);
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields, String[] propertys) {
return filterFields(fields, asList(propertys));
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields, String[] propertys,
String[] excludes) {
return filterFields(fields, propertys, asList(excludes));
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields, String[] propertys,
Collection<String> excludes) {
return filterFields(fields, asList(propertys), excludes);
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields,
Collection<String> propertys) {
return filterFields(fields, propertys, Collections.EMPTY_SET);
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields,
Collection<String> propertys, String[] excludes) {
return filterFields(fields, propertys, asList(excludes));
}
public LinkedHashMap<String, IField<Object>> filterFields(Fields fields,
Collection<String> propertys, Collection<String> excludes) {
LinkedHashMap<String, IField<Object>> ret = new LinkedHashMap<>();
if ((fields != null) && !fields.isEmpty()) {
List<TableMapper> tms = getTableMappers(getTable(), removeAll(propertys, excludes));
if ((tms != null) && !tms.isEmpty()) {
for (Entry<String, Field<Serializable>> entry : fields.entrySet()) {
List<String> columns = getFullColumns(entry.getKey(), tms, Flag.SELECT);
if ((columns == null) || columns.isEmpty()) {
continue;
}
for (String column : columns) {
if (isNotEmpty(column)) {
Field<Serializable> field = entry.getValue();
if (!field.isEmpty()) {
ret.put(column, field.getWrap());
if (!filterFieldsMulti()) {
break;
}
}
}
}
}
}
}
return ret;
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts) {
return filterSorts(sorts, Collections.EMPTY_SET);
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, String[] propertys) {
return filterSorts(sorts, asList(propertys));
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, String[] propertys,
String[] excludes) {
return filterSorts(sorts, propertys, asList(excludes));
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, String[] propertys,
Collection<String> excludes) {
return filterSorts(sorts, asList(propertys), excludes);
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, Collection<String> propertys) {
return filterSorts(sorts, propertys, Collections.EMPTY_SET);
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, Collection<String> propertys,
String[] excludes) {
return filterSorts(sorts, propertys, asList(excludes));
}
public LinkedHashMap<String, Boolean> filterSorts(Sorts sorts, Collection<String> propertys,
Collection<String> excludes) {
LinkedHashMap<String, Boolean> ret = new LinkedHashMap<>();
if ((sorts != null) && !sorts.isEmpty()) {
List<TableMapper> tms = getTableMappers(getTable(), removeAll(propertys, excludes));
if ((tms != null) && !tms.isEmpty()) {
for (Sort sort : sorts) {
List<String> columns = getFullColumns(sort.getKey(), tms, Flag.SELECT);
if ((columns == null) || columns.isEmpty()) {
continue;
}
for (String column : columns) {
if (isNotEmpty(column)) {
ret.put(column, sort.getAsc() ? Boolean.TRUE : Boolean.FALSE);
if (!filterSortsMulti()) {
break;
}
}
}
}
}
}
return ret;
}
public LinkedHashMap<String, IValue<Object>> filterInserts(Object object) {
Args.notNull(object, "object");
TableMapper tm = getTableMapper(getTable());
if (!tm.getBeanClass().isAssignableFrom(object.getClass())) {
throw new IllegalStateException(String.format("The Argument %s is not instanceof %s",
object.getClass().getName(), tm.getBeanClass().getName()));
}
LinkedHashMap<String, IValue<Object>> ret = new LinkedHashMap<>();
Map<String, ColumnDefine> fcs = tm.getFieldColumns(Flag.INSERT);
for (Entry<String, ColumnDefine> e : fcs.entrySet()) {
ColumnDefine cd = e.getValue();
Object value = MapperUtils.getPropertysValue(object, cd);
if ((value == null) && cd.isDefaultIfNull()) {
continue;
}
ret.put(cd.getName(), new ValueImpl<>(value));
}
return ret;
}
public LinkedHashMap<String, IValue<Object>> filterUpdates(Object object) {
return filterUpdates(object, false);
}
public LinkedHashMap<String, IValue<Object>> filterUpdatesAll(Object object) {
return filterUpdates(object, true);
}
private LinkedHashMap<String, IValue<Object>> filterUpdates(Object object, boolean all) {
Args.notNull(object, "object");
TableMapper tm = getTableMapper(getTable());
if (!tm.getBeanClass().isAssignableFrom(object.getClass())) {
throw new IllegalStateException(String.format("The Argument %s is not instanceof %s",
object.getClass().getName(), tm.getBeanClass().getName()));
}
LinkedHashMap<String, IValue<Object>> ret = new LinkedHashMap<>();
Set<String> primarys = null;
if (!all) {
primarys = new HashSet<>();
for (Entry<String, ColumnDefine> e : tm.getPrimarys().entrySet()) {
ColumnDefine cd = e.getValue();
primarys.add(cd.getName());
}
}
Map<String, ColumnDefine> fcs = tm.getFieldColumns(Flag.UPDATE);
for (Entry<String, ColumnDefine> e : fcs.entrySet()) {
ColumnDefine cd = e.getValue();
String name = cd.getName();
if ((primarys != null) && primarys.contains(name)) {
continue;
}
Object value = MapperUtils.getPropertysValue(object, cd);
if ((value == null) && cd.isDefaultIfNull()) {
continue;
}
ret.put(name, new ValueImpl<>(value));
}
return ret;
}
public LinkedHashMap<String, IValue<Object>> filterPrimarys(Object object) {
Args.notNull(object, "object");
TableMapper tm = getTableMapper(getTable());
if (!tm.getBeanClass().isAssignableFrom(object.getClass())) {
throw new IllegalStateException(String.format("The Argument %s is not instanceof %s",
object.getClass().getName(), tm.getBeanClass().getName()));
}
LinkedHashMap<String, IValue<Object>> ret = new LinkedHashMap<>();
Map<String, ColumnDefine> ps = tm.getPrimarys();
for (Entry<String, ColumnDefine> e : ps.entrySet()) {
ColumnDefine cd = e.getValue();
Object value = MapperUtils.getPropertysValue(object, cd);
ret.put(cd.getName(), new ValueImpl<>(value));
}
return ret;
}
protected <T> List<T> asList(T... objs) {
if ((objs == null) || (objs.length < 1)) {
return Collections.EMPTY_LIST;
}
return Arrays.asList(objs);
}
protected <T> List<T> removeAll(Collection<T> origin, Collection<T> remove) {
if ((origin == null) || origin.isEmpty()) {
return new ArrayList<>();
}
if ((remove == null) || remove.isEmpty()) {
return new ArrayList<>(origin);
}
List<T> ret = new ArrayList<>();
for (T obj : origin) {
if (!remove.contains(obj)) {
ret.add(obj);
}
}
return ret;
}
protected boolean filterFieldsMulti() {
return false;
}
protected boolean filterSortsMulti() {
return false;
}
protected List<String> getFullColumns(String field, List<TableMapper> tableMappers, Flag flag) {
List<String> ret = new ArrayList<>();
if (StringUtils.isNotEmpty(field) && (tableMappers != null) && !tableMappers.isEmpty()) {
if (!MapperUtils.isNestedField(field)) {
for (TableMapper tableMapper : tableMappers) {
if (tableMapper == null) {
continue;
}
String fc = null;
{
Map<String, String> sffcs = tableMapper.getStripFieldFullColumns(flag);
if ((sffcs != null) && sffcs.containsKey(field)) {
fc = sffcs.get(field);
}
}
if (StringUtils.isNotEmpty(fc)) {
ret.add(fc);
}
}
} else {
for (TableMapper tableMapper : tableMappers) {
if (tableMapper == null) {
continue;
}
String fc = null;
{
Map<String, String> pffcs = tableMapper.getPropertyFieldFullColumns(flag);
if ((pffcs != null) && pffcs.containsKey(field)) {
fc = pffcs.get(field);
}
}
if (StringUtils.isEmpty(fc)) {
Map<String, String> ffcs = tableMapper.getFieldFullColumns(flag);
if ((ffcs != null) && ffcs.containsKey(field)) {
fc = ffcs.get(field);
}
}
if (StringUtils.isNotEmpty(fc)) {
ret.add(fc);
}
}
}
}
return ret;
}
protected TableMapper getTableMapper(String table) {
List<TableMapper> tms = getTableMappers(table, null);
if ((tms == null) || tms.isEmpty()) {
return null;
}
if (1 < tms.size()) {
throw new IllegalStateException();
}
return tms.get(0);
}
protected List<TableMapper> getTableMappers(String table, Collection<String> propertys) {
List<TableMapper> ret = new ArrayList<>();
TableDefines defines = getDefines();
if ((defines != null) && !defines.isEmpty()) {
TableDefine define = defines.get(table);
if (define == null) {
throw new IllegalStateException(
String.format("not found TableDefine for %s", table));
}
ret.add(define.getTableMapper());
if ((propertys != null) && !propertys.isEmpty()) {
Map<String, TableMapper> propTms = define.getPropertyTableMappers();
if ((propTms != null) && !propTms.isEmpty()) {
for (String p : propertys) {
if (StringUtils.isEmpty(p)) {
continue;
}
TableMapper propTm = propTms.get(p);
if (propTm != null) {
ret.add(propTm);
}
}
}
}
}
return ret;
}
public boolean verify(String table, Map<String, String> columnTypes,
TypeComparator typeComparator) {
TableDefines defines = getDefines();
if ((defines != null) && !defines.isEmpty()) {
TableDefine define = defines.get(table);
if (define == null) {
throw new IllegalStateException(
String.format("not found TableDefine for %s", table));
}
return MapperUtils.verify(define.getTableMapper(), columnTypes, typeComparator);
}
return true;
}
protected abstract TableDefines getDefines();
public abstract Map<Class<?>, String> getTargetTables();
protected static class ValueImpl<T> implements IValue<T> {
private final T value;
public ValueImpl(T value) {
this.value = value;
}
@Override
public T getValue() {
return value;
}
}
}