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 @Override 070 public final <T> void setValueIfNonNull(String column, T value) 071 { 072 if(value != null){ 073 setValue(column,value); 074 } 075 } 076 @SuppressWarnings("unchecked") 077 @Override 078 public <T> T getValue(int columnID) 079 { 080 try { 081 return (T) metaData.getterMethods.get(columnID).invoke(this); 082 } catch (IndexOutOfBoundsException e) { 083 return null; 084 }catch (Exception e) { 085 Throwables.throwIfUnchecked(e); 086 throw new RuntimeException(e); 087 } 088 } 089 090 @Override 091 public <T> void setValue(int columnID,T value) 092 { 093 try { 094 metaData.setterMethods.get(columnID).invoke(this, value); 095 } catch (IndexOutOfBoundsException e) { 096 return ; 097 }catch (Exception e) { 098 Throwables.throwIfUnchecked(e); 099 throw new RuntimeException(e); 100 } 101 } 102 @Override 103 public final <T> void setValueIfNonNull(int columnID,T value) 104 { 105 if(value != null){ 106 setValue(columnID,value); 107 } 108 } 109 110 @Override 111 public Object[] primaryValues() 112 { 113 Object[] values = new Object[metaData.primaryKeyCount]; 114 for(int i = 0; i < values.length; ++i) { 115 values[i] = getValue(metaData.primaryKeyIds[i]); 116 } 117 return values; 118 } 119 120 @Override 121 public <T> T primaryValue() 122 { 123 if(metaData.primaryKeyCount != 1) { 124 throw new UnsupportedOperationException(); 125 } 126 return getValue(metaData.primaryKeyIds[0]); 127 } 128 129 @Override 130 public Object[] asValueArray(int...columnIds) 131 { 132 if(null == columnIds || columnIds.length == 0){ 133 columnIds = metaData.defaultColumnIdList; 134 } 135 Object[] v = new Object[columnIds.length]; 136 for(int i=0; i< v.length; ++i){ 137 v[i] = getValue(columnIds[i]); 138 } 139 return v; 140 } 141 @Override 142 public Map<String, Object> asNameValueMap(){ 143 // double check 144 if(mapView == null){ 145 synchronized (this) { 146 if(mapView == null){ 147 mapView = new RowMapView(this); 148 } 149 } 150 } 151 return mapView; 152 } 153 154 @Override 155 public <B extends BaseBean> B copy(B bean) 156 { 157 return copy(bean,new int[0]); 158 } 159 160 @SuppressWarnings("unchecked") 161 @Override 162 public <B extends BaseBean> B copy(B bean, int... fieldList) 163 { 164 if(bean != null && bean != this){ 165 if (null == fieldList || 0 == fieldList.length){ 166 fieldList = metaData.defaultColumnIdList; 167 } 168 for (int columnId:fieldList) { 169 if( bean.isInitialized(columnId) && !Objects.deepEquals(bean.getValue(columnId), getValue(columnId))){ 170 setValue(columnId, bean.getValue(columnId)); 171 } 172 } 173 } 174 return (B)this; 175 } 176 177 @Override 178 public <B extends BaseBean> B copy(B bean, String... fieldList) 179 { 180 return copy(bean, 181 (null == fieldList) 182 ? null 183 : Ints.toArray(Lists.transform(Arrays.asList(fieldList), metaData.COLUMNID_FUN))); 184 } 185 186 @Override 187 @SuppressWarnings("unchecked") 188 public <B extends BaseBean,F extends BaseBean> B copy(F from,Map<Integer,Integer> columnsMap){ 189 for(Map.Entry<Integer, Integer> entry : checkNotNull(columnsMap,"columnsMap is null").entrySet()){ 190 setValue(entry.getValue(), null == from ? null : from.getValue(entry.getKey())); 191 } 192 return (B) this; 193 } 194 195 @SuppressWarnings("unchecked") 196 public <B extends BaseBean> B copy(Map<Integer, Object>values){ 197 if(null != values){ 198 for(Entry<Integer, Object> entry : values.entrySet()){ 199 setValue(entry.getKey().intValue(), entry.getValue()); 200 } 201 } 202 return (B) this; 203 } 204 205 @Override 206 public final String tableName() { 207 return metaData.tablename; 208 } 209 210 /** 211 * cast byte array to HEX string 212 * 213 * @param input 214 * @return {@code null} if {@code input} is null 215 */ 216 private static final String toHex(byte[] input) { 217 if (null == input){ 218 return null; 219 } 220 StringBuffer sb = new StringBuffer(input.length * 2); 221 for (int i = 0; i < input.length; i++) { 222 sb.append(Character.forDigit((input[i] & 240) >> 4, 16)); 223 sb.append(Character.forDigit(input[i] & 15, 16)); 224 } 225 return sb.toString(); 226 } 227 private static final StringBuilder append(StringBuilder buffer,boolean full,byte[] value){ 228 if(full || null == value){ 229 buffer.append(toHex(value)); 230 }else{ 231 buffer.append(value.length).append(" bytes"); 232 } 233 return buffer; 234 } 235 private static int stringLimit = 64; 236 private static final int MINIMUM_LIMIT = 16; 237 private static final StringBuilder append(StringBuilder buffer,boolean full,String value){ 238 if(full || null == value || value.length() <= stringLimit){ 239 buffer.append(value); 240 }else{ 241 buffer.append(value.substring(0,stringLimit - 8)).append(" ...").append(value.substring(stringLimit-4,stringLimit)); 242 } 243 return buffer; 244 } 245 private static final StringBuilder append(StringBuilder buffer,boolean full,Object value){ 246 if(value instanceof String){ 247 return append(buffer, full, (String)value); 248 } 249 if(value instanceof byte[]){ 250 return append(buffer, full, (byte[])value); 251 } 252 return buffer.append(value); 253 } 254 public static final void setStringLimit(int limit){ 255 checkArgument(limit >= MINIMUM_LIMIT, "INVALID limit %s,minimum value %s",limit,MINIMUM_LIMIT); 256 stringLimit = limit; 257 } 258 @Override 259 public String toString(boolean notNull, boolean fullIfStringOrBytes) { 260 StringBuilder builder = new StringBuilder(this.getClass().getName()).append("@").append(Integer.toHexString(this.hashCode())).append("["); 261 int count = 0; 262 for(int i = 0; i < metaData.columnCount; ++i){ 263 Object value = getValue(i); 264 if( !notNull || null != value){ 265 if(count > 0){ 266 builder.append(","); 267 } 268 builder.append(metaData.columnNameOf(i)).append("="); 269 append(builder,fullIfStringOrBytes,value); 270 ++count; 271 } 272 } 273 builder.append("]"); 274 return builder.toString(); 275 } 276 @Override 277 public boolean equals(Object object) { 278 if(!metaData.beanType.isInstance(object)){ 279 return false; 280 } 281 BaseBean bean = (BaseBean)object; 282 EqualsBuilder equalsBuilder = new EqualsBuilder(); 283 for(int i=0; i<metaData.columnCount; ++i){ 284 equalsBuilder.append((Object)getValue(i), (Object)bean.getValue(i)); 285 } 286 return equalsBuilder.isEquals(); 287 } 288 @Override 289 public int hashCode() { 290 HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(-82280557, -700257973); 291 int[] hashIds = metaData.primaryKeyCount > 0 ? metaData.primaryKeyIds : metaData.defaultColumnIdList; 292 for(int i=0; i<hashIds.length; ++i){ 293 hashCodeBuilder.append((Object)getValue(hashIds[i])); 294 } 295 return hashCodeBuilder.toHashCode(); 296 } 297 298 @Override 299 public String toString() { 300 return toString(true,false); 301 } 302 303 @Override 304 public int compareTo(BaseRow object){ 305 CompareToBuilder compareToBuilder = new CompareToBuilder(); 306 for(int i=0; i<metaData.columnCount; ++i){ 307 compareToBuilder.append((Object)getValue(i), (Object)object.getValue(i)); 308 } 309 return compareToBuilder.toComparison(); 310 } 311 @Override 312 public BaseRow clone(){ 313 try { 314 return (BaseRow) super.clone(); 315 } catch (CloneNotSupportedException e) { 316 throw new RuntimeException(e); 317 } 318 } 319 320}