001package gu.sql2java; 002 003import java.util.Arrays; 004import java.util.Map; 005import java.util.Objects; 006import java.util.Map.Entry; 007 008import com.google.common.base.Preconditions; 009import com.google.common.base.Throwables; 010import com.google.common.collect.Lists; 011import com.google.common.primitives.Ints; 012 013import static com.google.common.base.Preconditions.*; 014 015/** 016 * abstract implementation of {@link BaseBean} 017 * @author guyadong 018 * 019 */ 020public abstract class BaseRow implements BaseBean,Comparable<BaseRow>,Cloneable { 021 022 protected final RowMetaData metaData; 023 private volatile Map<String, Object> mapView; 024 protected BaseRow(RowMetaData metaData) { 025 this.metaData = checkNotNull(metaData,"metaData is null"); 026 } 027 protected BaseRow() { 028 this.metaData = checkNotNull(RowMetaData.getMetaData(getClass()),"metaData is null"); 029 } 030 @Override 031 public final boolean isInitialized(String column) { 032 return isInitialized(metaData.columnIDOf(column)); 033 } 034 035 @Override 036 public boolean beModified() { 037 for(int i=0; i<metaData.columnCount; ++i){ 038 if(isModified(i)){ 039 return true; 040 } 041 } 042 return false; 043 } 044 045 @Override 046 public boolean isModified(String column){ 047 return isModified(metaData.columnIDOf(column)); 048 } 049 050 @Override 051 public final <T> T getValue(String column) { 052 return getValue(metaData.columnIDOf(column)); 053 } 054 055 @Override 056 public final <T> T getValueChecked(int columnID) { 057 T value = getValue(columnID); 058 return Preconditions.checkNotNull(value,"value of columnid %s IS NULL", columnID); 059 } 060 @Override 061 public final <T> T getValueChecked(String column) { 062 T value = getValue(column); 063 return Preconditions.checkNotNull(value,"value of column %s IS NULL", column); 064 } 065 @Override 066 public final <T> void setValue(String column, T value) { 067 setValue(metaData.columnIDOf(column),value); 068 } 069 @SuppressWarnings("unchecked") 070 @Override 071 public <T> T getValue(int columnID) 072 { 073 try { 074 return (T) metaData.getterMethods.get(columnID).invoke(this); 075 } catch (IndexOutOfBoundsException e) { 076 return null; 077 }catch (Exception e) { 078 Throwables.throwIfUnchecked(e); 079 throw new RuntimeException(e); 080 } 081 } 082 083 @Override 084 public <T> void setValue(int columnID,T value) 085 { 086 try { 087 metaData.setterMethods.get(columnID).invoke(this, value); 088 } catch (IndexOutOfBoundsException e) { 089 return ; 090 }catch (Exception e) { 091 Throwables.throwIfUnchecked(e); 092 throw new RuntimeException(e); 093 } 094 } 095 @Override 096 public Object[] primaryValues() 097 { 098 Object[] values = new Object[metaData.primaryKeyCount]; 099 for(int i = 0; i < values.length; ++i) { 100 values[i] = getValue(metaData.primaryKeyIds[i]); 101 } 102 return values; 103 } 104 105 @Override 106 public <T> T primaryValue() 107 { 108 if(metaData.primaryKeyCount != 1) { 109 throw new UnsupportedOperationException(); 110 } 111 return getValue(metaData.primaryKeyIds[0]); 112 } 113 114 @Override 115 public Object[] asValueArray(int...columnIds) 116 { 117 if(null == columnIds || columnIds.length == 0){ 118 columnIds = metaData.defaultColumnIdList; 119 } 120 Object[] v = new Object[columnIds.length]; 121 for(int i=0; i< v.length; ++i){ 122 v[i] = getValue(columnIds[i]); 123 } 124 return v; 125 } 126 @Override 127 public Map<String, Object> asNameValueMap(){ 128 // double check 129 if(mapView == null){ 130 synchronized (this) { 131 if(mapView == null){ 132 mapView = new RowMapView(this); 133 } 134 } 135 } 136 return mapView; 137 } 138 139 @Override 140 public <B extends BaseBean> B copy(B bean) 141 { 142 return copy(bean,new int[0]); 143 } 144 145 @SuppressWarnings("unchecked") 146 @Override 147 public <B extends BaseBean> B copy(B bean, int... fieldList) 148 { 149 if(bean != null && bean != this){ 150 if (null == fieldList || 0 == fieldList.length){ 151 fieldList = metaData.defaultColumnIdList; 152 } 153 for (int columnId:fieldList) { 154 if( bean.isInitialized(columnId) && !Objects.deepEquals(bean.getValue(columnId), getValue(columnId))){ 155 setValue(columnId, bean.getValue(columnId)); 156 } 157 } 158 } 159 return (B)this; 160 } 161 162 @Override 163 public <B extends BaseBean> B copy(B bean, String... fieldList) 164 { 165 return copy(bean, 166 (null == fieldList) 167 ? null 168 : Ints.toArray(Lists.transform(Arrays.asList(fieldList), metaData.COLUMNID_FUN))); 169 } 170 171 @Override 172 @SuppressWarnings("unchecked") 173 public <B extends BaseBean,F extends BaseBean> B copy(F from,Map<Integer,Integer> columnsMap){ 174 for(Map.Entry<Integer, Integer> entry : checkNotNull(columnsMap,"columnsMap is null").entrySet()){ 175 setValue(entry.getValue(), null == from ? null : from.getValue(entry.getKey())); 176 } 177 return (B) this; 178 } 179 180 @SuppressWarnings("unchecked") 181 public <B extends BaseBean> B copy(Map<Integer, Object>values){ 182 if(null != values){ 183 for(Entry<Integer, Object> entry : values.entrySet()){ 184 setValue(entry.getKey().intValue(), entry.getValue()); 185 } 186 } 187 return (B) this; 188 } 189 190 @Override 191 public final String tableName() { 192 return metaData.tablename; 193 } 194 195 /** 196 * cast byte array to HEX string 197 * 198 * @param input 199 * @return {@code null} if {@code input} is null 200 */ 201 private static final String toHex(byte[] input) { 202 if (null == input){ 203 return null; 204 } 205 StringBuffer sb = new StringBuffer(input.length * 2); 206 for (int i = 0; i < input.length; i++) { 207 sb.append(Character.forDigit((input[i] & 240) >> 4, 16)); 208 sb.append(Character.forDigit(input[i] & 15, 16)); 209 } 210 return sb.toString(); 211 } 212 private static final StringBuilder append(StringBuilder buffer,boolean full,byte[] value){ 213 if(full || null == value){ 214 buffer.append(toHex(value)); 215 }else{ 216 buffer.append(value.length).append(" bytes"); 217 } 218 return buffer; 219 } 220 private static int stringLimit = 64; 221 private static final int MINIMUM_LIMIT = 16; 222 private static final StringBuilder append(StringBuilder buffer,boolean full,String value){ 223 if(full || null == value || value.length() <= stringLimit){ 224 buffer.append(value); 225 }else{ 226 buffer.append(value.substring(0,stringLimit - 8)).append(" ...").append(value.substring(stringLimit-4,stringLimit)); 227 } 228 return buffer; 229 } 230 private static final StringBuilder append(StringBuilder buffer,boolean full,Object value){ 231 if(value instanceof String){ 232 return append(buffer, full, (String)value); 233 } 234 if(value instanceof byte[]){ 235 return append(buffer, full, (byte[])value); 236 } 237 return buffer.append(value); 238 } 239 public static final void setStringLimit(int limit){ 240 checkArgument(limit >= MINIMUM_LIMIT, "INVALID limit %s,minimum value %s",limit,MINIMUM_LIMIT); 241 stringLimit = limit; 242 } 243 @Override 244 public String toString(boolean notNull, boolean fullIfStringOrBytes) { 245 StringBuilder builder = new StringBuilder(this.getClass().getName()).append("@").append(Integer.toHexString(this.hashCode())).append("["); 246 int count = 0; 247 for(int i = 0; i < metaData.columnCount; ++i){ 248 Object value = getValue(i); 249 if( !notNull || null != value){ 250 if(count > 0){ 251 builder.append(","); 252 } 253 builder.append(metaData.columnNameOf(i)).append("="); 254 append(builder,fullIfStringOrBytes,value); 255 ++count; 256 } 257 } 258 builder.append("]"); 259 return builder.toString(); 260 } 261 @Override 262 public boolean equals(Object object) { 263 if(!metaData.beanType.isInstance(object)){ 264 return false; 265 } 266 BaseBean bean = (BaseBean)object; 267 EqualsBuilder equalsBuilder = new EqualsBuilder(); 268 for(int i=0; i<metaData.columnCount; ++i){ 269 equalsBuilder.append((Object)getValue(i), (Object)bean.getValue(i)); 270 } 271 return equalsBuilder.isEquals(); 272 } 273 @Override 274 public int hashCode() { 275 HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(-82280557, -700257973); 276 int[] hashIds = metaData.primaryKeyCount > 0 ? metaData.primaryKeyIds : metaData.defaultColumnIdList; 277 for(int i=0; i<hashIds.length; ++i){ 278 hashCodeBuilder.append((Object)getValue(hashIds[i])); 279 } 280 return hashCodeBuilder.toHashCode(); 281 } 282 283 @Override 284 public String toString() { 285 return toString(true,false); 286 } 287 288 @Override 289 public int compareTo(BaseRow object){ 290 CompareToBuilder compareToBuilder = new CompareToBuilder(); 291 for(int i=0; i<metaData.columnCount; ++i){ 292 compareToBuilder.append((Object)getValue(i), (Object)object.getValue(i)); 293 } 294 return compareToBuilder.toComparison(); 295 } 296 @Override 297 public BaseRow clone(){ 298 try { 299 return (BaseRow) super.clone(); 300 } catch (CloneNotSupportedException e) { 301 throw new RuntimeException(e); 302 } 303 } 304 305}