All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.BaseRow Maven / Gradle / Ivy

There is a newer version: 5.3.2
Show newest version
package gu.sql2java;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Map.Entry;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import gu.sql2java.exception.UnsupportTypeException;

import static com.google.common.base.Preconditions.*;
import static gu.sql2java.BaseTypeColumnCodec.BASE_CODEC;
import static gu.sql2java.BaseTypeColumnCodec.isBaseColumnType;
import static gu.sql2java.utils.DeepCloneUtils.cloneFields;

/**
 * abstract implementation of {@link BaseBean}
 * @author guyadong
 *
 */
public abstract class BaseRow implements BaseBean,Comparable, Cloneable {

	protected final RowMetaData metaData;
	private volatile Map mapView;
	protected BaseRow(RowMetaData metaData) {
		this.metaData = checkNotNull(metaData,"metaData is null");
	}
	protected BaseRow() {
		this.metaData = checkNotNull(RowMetaData.getMetaData(getClass()),"metaData is null");
	}
	@Override
	public final boolean isInitialized(String column) {
		return isInitialized(metaData.columnIDOf(column));
	}
	
	@Override
	public boolean beModified() {
		for(int i=0; i 0) {
    		for(int i=0; i < columnIDs.length; ++i){
    			if(isModified(columnIDs[i])){
    				return true;
    			}
    		}
    	}
        return false;
    }
    @Override
    public boolean isModified(String... columns){
    	if(null != columns && columns.length > 0) {
    		for(int i=0; i < columns.length; ++i){
    			if(isModified(columns[i])){
    				return true;
    			}
    		}
    	}
    	return false;
    }

    @Override
    public boolean isModified(String column){        
        return isModified(metaData.columnIDOf(column));
    }
    @Override
    public boolean isModifiedNested(String nestedName){
        if(null == nestedName){
            return false;
        }
        if(isModified(nestedName)){
            return true;
        }
        String prefix = metaData.tablename + ".";
        if(nestedName.startsWith(prefix)){
            /** remove table name */
            nestedName = nestedName.substring(prefix.length());
        }
        int firstDot = nestedName.indexOf('.');
        if(firstDot > 0){
            /** 0-firstDot is column name */
            return isModified(nestedName.substring(0, firstDot));
        } if(firstDot == 0){
            /** column name is empty */
            return false;
        }else {
            /**  only column name */
            return isModified(nestedName);
        }
    }

    @Override
	public int[] modifiedColumnIDs(){
    	List list = Lists.newArrayListWithCapacity(metaData.columnCount);
    	for(int columnID = 0; columnID < metaData.columnCount;++columnID){
    		if(isModified(columnID)){
    			list.add(columnID);	
    		}
    	}
    	return Ints.toArray(list);
    }
    @Override
	public String[] modifiedColumns(){
    	return Iterables.toArray(metaData.columnNamesOf(modifiedColumnIDs()),String.class);
    }
    @Override
	public int modifiedColumnCount(){
    	return modifiedColumnIDs().length;
    }
	@Override
	public final  T getValue(String column) {
		return getValue(metaData.columnIDOf(column));
	}

	@Override
	public final  T getValueChecked(int columnID) {
		T value = getValue(columnID);
		return Preconditions.checkNotNull(value,"value of columnid %s IS NULL", columnID);
	}
	@Override
	public final  T getValueChecked(String column) {
		T value = getValue(column);
		return Preconditions.checkNotNull(value,"value of column %s IS NULL", column);
	}
	@Override
	public final void setValue(String column, Object value) {
		setValue(metaData.columnIDOf(column),value);
	}
    @Override
    public final boolean setValueIfNonNull(String column, Object value) 
    {
    	return setValueIf(value != null,column,value);
    }
    
    @Override
    public final boolean setValueIfNonEqual(String column, Object value) 
    {
       return setValueIf(!Objects.equals(value, getValue(column)),column,value);
    }
    @Override
    public final boolean setValueIf(boolean expression,String column, Object value) 
    {
        if(expression){
            setValue(column,value);
        }
        return expression;
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public  T getValue(int columnID)
    {
    	try {
			return (T) metaData.getterMethods.get(columnID).invoke(this);
		} catch (IndexOutOfBoundsException e) {
			return null;
		}catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
    }
    @SuppressWarnings("unchecked")
	public  T getOriginValue(int columnID)
    {
    	try {
    		Class fieldType = metaData.fieldTypeOf(columnID);
    		Method getter = metaData.getterMethods.get(columnID);
    		if(getter.getReturnType().isAssignableFrom(fieldType)) {
    			return (T)getter.invoke(this);
    		}else {
    			Method reader = metaData.readMethods.get(columnID);
    			checkState(null != reader,"NOT DEFINED read method for %s",metaData.columnNameOf(columnID));
    			return (T) reader.invoke(this);
    		}
    	} catch (IndexOutOfBoundsException e) {
    		return null;
    	}catch (Exception e) {
    		Throwables.throwIfUnchecked(e);
    		throw new RuntimeException(e);
    	}
    }
    @Override
	@SuppressWarnings("unchecked")
	public  T getJdbcValue(int columnID)
    {
    	try {
    		Class jdbcType = metaData.jdbcTypeOf(columnID);
    		Method getter = metaData.getterMethods.get(columnID);
    		if(getter.getReturnType().isAssignableFrom(jdbcType)) {
    			return (T)getter.invoke(this);
    		}    		
    		Method reader = metaData.readMethods.get(columnID);
    		if(null != reader) {
    			if(reader.getReturnType().isAssignableFrom(jdbcType)) {
    				return (T) reader.invoke(this);
    			}
    			if(isBaseColumnType(reader.getReturnType())) {
    				return (T) BASE_CODEC.serialize(reader.invoke(this), jdbcType);
    			}
    		}
    		ColumnCodec codec = metaData.columnCodecOf(columnID);
    		if(null != codec) {
    			return (T) codec.serialize(getOriginValue(columnID), jdbcType);
    		}
    		throw new IllegalStateException(String.format("CAN NOT GET  VALUE OF %d AS %s",columnID,jdbcType));
    	} catch (IndexOutOfBoundsException e) {
    		return null;
    	}catch (Exception e) {
    		Throwables.throwIfUnchecked(e);
    		throw new RuntimeException(e);
    	}
    }
    
    private  void setValue(Method setMethod,T value) throws IllegalArgumentException
	{
		try {
			setMethod.invoke(this, value);
		} catch ( IllegalArgumentException e) {
			if(isBaseColumnType(value)) {
				Class expectType = setMethod.getParameters()[0].getType();
				if(isBaseColumnType(expectType)) {
					try {
						setValue(setMethod,BASE_CODEC.deserialize(value, expectType));
						return;
					} catch (UnsupportTypeException e2) {
						// DO NOTHING
					}
				}
			}
			throw e;
		}catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	@Override
    public  void setValue(int columnID,T value)
    {
    	try {
    		setValue(metaData.setterMethods.get(columnID), value);
		} catch (IndexOutOfBoundsException e) {
			return ;
		}catch ( IllegalArgumentException e) {
			Method writeMethod = metaData.writeMethods.get(columnID);
			if(null != writeMethod) {
				setValue(writeMethod, value);
				return;
			}
		    throw e;
        }
    }
    @Override
    public final  boolean setValueIfNonNull(int columnID,T value)
    {
        return setValueIf(value != null,columnID,value);
    }
    @Override
    public final  boolean setValueIfNonEqual(int columnID,T value)
    {
        return setValueIf(!Objects.equals(value, getValue(columnID)),columnID,value);
    }
    @Override
    public final  boolean setValueIf(boolean expression,int columnID,T value)
    {
        if(expression){
            setValue(columnID,value);
        }
        return expression;
    }
    
    @Override
    public Object[] primaryValues()
    {
        Object[] values = new Object[metaData.primaryKeyCount];
        for(int i = 0; i < values.length; ++i) {
            values[i] = getValue(metaData.primaryKeyIds[i]);
        }
        return values;
    }

    @Override
    public  T primaryValue()
    {
        if(metaData.primaryKeyCount != 1) {
            throw new UnsupportedOperationException();
        }
        return getValue(metaData.primaryKeyIds[0]);
    }
    
    @Override
    public Object[] asValueArray(int...columnIds)
    {
		if(null == columnIds || columnIds.length == 0){
			columnIds = metaData.defaultColumnIdList;
		}
        Object[] v = new Object[columnIds.length];
        for(int i=0; i< v.length; ++i){
        	v[i] = getValue(columnIds[i]);
        }
        return v;
    }
	@Override
	public Map asNameValueMap(){
		// double check 
		if(mapView == null){
			synchronized (this) {
				if(mapView == null){
					mapView = new RowMapView(this);
				}
			}
		}
		return mapView;
	}
	@Override
	public Map asNameValueMap(boolean ignoreNull,String ...ignoreColumns){
		return asNameValueMap(ignoreNull, false,null != ignoreColumns ? Arrays.asList(ignoreColumns) : Collections.emptySet());
	}
	@Override
	public Map asNameValueMap(boolean ignoreNull,IterableignoreColumns){
		return asNameValueMap(ignoreNull,false,ignoreColumns);
	}
	@Override
	public Map asNameValueMap(boolean ignoreNull,boolean include,String ...includeColumns){
		return asNameValueMap(ignoreNull, include,null != includeColumns ? Arrays.asList(includeColumns) : Collections.emptySet());
	}
	@Override
	public Map asNameValueMap(boolean ignoreNull,final boolean include,Iterablecolumns){
		Map map = asNameValueMap();
		if(ignoreNull){
			map = Maps.filterValues(map, Predicates.notNull());
		}
		if(null != columns){
			final Set names = Sets.filter(Sets.newHashSet(columns),Predicates.notNull());
			map = Maps.filterKeys(map, new Predicate() {
				@Override
				public boolean apply(String input) {
					return include ? names.contains(input) : !names.contains(input);
				}
			});
		}
		return Maps.newLinkedHashMap(map);
	}
	
	@Override
    public  B copy(B bean)
    {
        return copy(bean,new int[0]);
    }

    @SuppressWarnings("unchecked")
    @Override
    public  B copy(B bean, int... fieldList)
    {   
    	if(bean != null && bean != this){
    		for (int columnId:metaData.validColumnIDsOrAll(fieldList)) {
    		    // valid column id only 
    		    if( bean.isInitialized(columnId) && !Objects.deepEquals(bean.getValue(columnId), getValue(columnId))){
    		        setValue(columnId, bean.getValue(columnId));
    		    }
    		}
    	}
        return (B)this;
    }

    @Override
    public  B copy(B bean, String... fieldList)
    {
        return copy(bean, null, fieldList);
    }
    @SuppressWarnings("unchecked")
    @Override
	public  B copy(B bean, Predicate fieldFilter,int... fieldList)
    {
        int[] columnIds = filterColumnIDs(fieldFilter,fieldList);
        if (null == columnIds || 0 == columnIds.length){
            return (B) this; 
        }
        return copy(bean, columnIds);
    }
    @SuppressWarnings("unchecked")
    @Override
    public  B copy(B bean, Predicate fieldFilter,String... fieldList)
    {
        int[] columnIds = filterColumnIDs(fieldFilter,fieldList);
        if (null == columnIds || 0 == columnIds.length){
            return (B) this; 
        }
        return copy(bean, columnIds);
    }

    @Override
	@SuppressWarnings("unchecked")
	public  B copy(F from,Map columnsMap){
        for(Map.Entry entry : checkNotNull(columnsMap,"columnsMap is null").entrySet()){
        	setValue(entry.getValue(), null == from ? null : from.getValue(entry.getKey()));
        }
    	return (B) this;
    }
    
    @SuppressWarnings("unchecked")
	public  B copy(Mapvalues){
    	if(null != values){
    		for(Entry entry : values.entrySet()){
    			setValue(entry.getKey().intValue(), entry.getValue());
    		}
    	}
    	return (B) this;
    }
    @Override
    public boolean equalColumn(Object object,int columnId)
    {   
        if(!metaData.beanType.isInstance(object)){
            return false;
        }        
        BaseBean bean = (BaseBean)object; 
        if(null != bean){
            if(bean != this){
                if(metaData.isValidColumnID(columnId)){
                    // valid column id only 
                    if( bean.isInitialized(columnId) && !Objects.deepEquals(bean.getValue(columnId), getValue(columnId))){
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }
    @Override
    public boolean equalColumn(Object object, int... fieldList)
    {   
        if(metaData.beanType.isInstance(object)){
            BaseBean bean = (BaseBean)object;
            if(bean != this && null != fieldList){
                for (int columnId:fieldList) {
                    // valid column id only 
                    if(metaData.isValidColumnID(columnId)){
                        if( bean.isInitialized(columnId) && !Objects.deepEquals(bean.getValue(columnId), getValue(columnId))){
                            return false;
                        }
                    }
                }
            }
            return true;
        }
        return false;
    }
    @Override
    public boolean equalColumn(Object object, Predicate fieldFilter,int... fieldList)
    {
        return equalColumn(object, filterColumnIDs(fieldFilter,fieldList));
    }
    @Override
    public boolean equalColumn(Object object, Predicate fieldFilter,String... fieldList)
    {
        return equalColumn(object, filterColumnIDs(fieldFilter,fieldList));
    }
    private int[] filterColumnIDs(Predicate fieldFilter,int... fieldList){
        if(null != fieldList){
            if(null != fieldFilter){
                Iterable columnIDs = Iterables.filter(Ints.asList(fieldList),fieldFilter);
                return Ints.toArray(Lists.newArrayList(columnIDs));
            }else {
                return fieldList;
            }
        }
        return null;
    }
    private int[] filterColumnIDs(Predicate fieldFilter,String... fieldList)
    {
        if(null != fieldList){
            Iterable filtered = Iterables.filter(Arrays.asList(fieldList),Predicates.notNull());
            if(null != fieldFilter){
                filtered = Iterables.filter(filtered,fieldFilter);
            }
            Iterable columnIDs = Iterables.transform(filtered, metaData.COLUMNID_FUN);
            return Ints.toArray(Lists.newArrayList(columnIDs));
        }
        return null;
    }

    @Override
	public final String tableName() {
		return metaData.tablename;
	}

	/**
     * cast byte array to HEX string
     * 
     * @param input
     * @return {@code null} if {@code input} is null
     */
    private static final String toHex(byte[] input) {
        if (null == input){
            return null;
        }
        StringBuffer sb = new StringBuffer(input.length * 2);
        for (int i = 0; i < input.length; i++) {
            sb.append(Character.forDigit((input[i] & 240) >> 4, 16));
            sb.append(Character.forDigit(input[i] & 15, 16));
        }
        return sb.toString();
    }
    private static final StringBuilder append(StringBuilder buffer,boolean full,byte[] value){
        if(full || null == value){
            buffer.append(toHex(value));
        }else{
            buffer.append(value.length).append(" bytes");
        }
        return buffer;
    }
    private static int stringLimit = 64;
    private static final int MINIMUM_LIMIT = 16;
    private static final StringBuilder append(StringBuilder buffer,boolean full,String value){
        if(full || null == value || value.length() <= stringLimit){
            buffer.append(value);
        }else{
            buffer.append(value.substring(0,stringLimit - 8)).append(" ...").append(value.substring(stringLimit-4,stringLimit));
        }
        return buffer;
    }
    private static final StringBuilder append(StringBuilder buffer,boolean full,Object value){
    	if(value instanceof String){
    		return append(buffer, full, (String)value);
    	}
    	if(value instanceof byte[]){
    		return append(buffer, full, (byte[])value);
    	}
        return buffer.append(value);
    }
    public static final void setStringLimit(int limit){
        checkArgument(limit >= MINIMUM_LIMIT, "INVALID limit %s,minimum value %s",limit,MINIMUM_LIMIT);
        stringLimit = limit;
    }
	@Override
	public String toString(boolean notNull, boolean fullIfStringOrBytes) {
        StringBuilder builder = new StringBuilder(this.getClass().getName()).append("@").append(Integer.toHexString(this.hashCode())).append("[");
        int count = 0;
        for(int i = 0; i < metaData.columnCount; ++i){
        	Object value = getValue(i);
        	if( !notNull || null != value){
            	if(count > 0){
            		builder.append(",");
            	}
        		builder.append(metaData.columnNameOf(i)).append("=");
        		append(builder,fullIfStringOrBytes,value);
        		++count;
        	}
        }
        builder.append("]");        
		return builder.toString();
	}
	@Override
	public boolean equals(Object object) {
		if(!metaData.beanType.isInstance(object)){
			return false;
		}
		BaseBean bean = (BaseBean)object;
		EqualsBuilder equalsBuilder = new EqualsBuilder();
		for(int i=0; i 0 ? metaData.primaryKeyIds : metaData.defaultColumnIdList;
		for(int i=0; i limit){
	                        setValue(columnID, new String(bytes, 0, limit));
	                    }
	                }
	            }else if(value instanceof byte[]) {
	                byte[] bytes = (byte[])value;
	                if(bytes.length > limit){
	                    setValue(columnID, Arrays.copyOf(bytes,limit));
	                }
	            }else if(value instanceof ByteBuffer) {
	                byte[] bytes = Sql2javaSupport.getBytesInBuffer((ByteBuffer) value);
	                if(bytes.length > limit){
                        setValue(columnID, ByteBuffer.wrap(Arrays.copyOf(bytes,limit)));
                    }
	            }
	        }
	    }
	}
	/**
     * truncate String,binary field 
     * @param column column name or field name
     * @since 3.17.7
     */
	public void truncate(String column) {
	    truncate(metaData.columnIDOf(column));
	}
}