001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019 020package org.nuxeo.ecm.core.storage.sql; 021 022import java.io.Serializable; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.LinkedList; 026import java.util.List; 027 028/** 029 * A type of fragment corresponding to a single row in a table and its associated in-memory information (state, dirty 030 * fields, attached context). 031 */ 032public final class SimpleFragment extends Fragment { 033 034 private static final long serialVersionUID = 1L; 035 036 private static final Row UNKNOWN_ROW = new Row(null, (Serializable) null); 037 038 public static final SimpleFragment UNKNOWN = new SimpleFragment(UNKNOWN_ROW, State.DETACHED, null); 039 040 /** 041 * Constructs a {@link SimpleFragment} from a {@link Row}. 042 * 043 * @param row the row, or {@code null} 044 * @param state the initial state for the fragment 045 * @param context the persistence context to which the fragment is tied, or {@code null} 046 */ 047 public SimpleFragment(Row row, State state, PersistenceContext context) { 048 super(row, state, context); 049 } 050 051 @Override 052 protected State refetch() { 053 Row newrow = context.mapper.readSimpleRow(row); 054 if (newrow == null) { 055 row.size = 0; 056 return State.ABSENT; 057 } else { 058 row = newrow; 059 clearDirty(); 060 return State.PRISTINE; 061 } 062 } 063 064 @Override 065 protected State refetchDeleted() { 066 row.size = 0; 067 return State.ABSENT; 068 } 069 070 /** 071 * Gets a value by key. 072 * 073 * @param key the key 074 * @return the value 075 */ 076 public Serializable get(String key) { 077 accessed(); 078 return row.get(key); 079 } 080 081 /** 082 * Puts a value by key. 083 * 084 * @param key the key 085 * @param value the value 086 */ 087 public void put(String key, Serializable value) { 088 accessed(); // maybe refetch other values 089 row.put(key, value); 090 // resize olddata to follow row if needed 091 if (oldvalues.length < row.values.length) { 092 Serializable[] tmp = oldvalues; 093 oldvalues = new Serializable[row.values.length]; 094 System.arraycopy(tmp, 0, oldvalues, 0, tmp.length); 095 } 096 if (getState() != State.ABSENT || value != null) { 097 // don't mark modified when setting null in an absent fragment 098 // to avoid creating unneeded rows 099 markModified(); 100 } 101 } 102 103 /** 104 * Returns a {@code String} value. 105 * 106 * @param key the key 107 * @return the value as a {@code String} 108 * @throws ClassCastException if the value is not a {@code String} 109 */ 110 public String getString(String key) { 111 return (String) get(key); 112 } 113 114 /** 115 * Gets the dirty keys (keys of values changed since last clear). 116 * 117 * @return the dirty keys 118 */ 119 public List<String> getDirtyKeys() { 120 List<String> keys = null; 121 for (int i = 0; i < row.size; i++) { 122 if (!same(oldvalues[i], row.values[i])) { 123 if (keys == null) { 124 keys = new LinkedList<String>(); 125 } 126 keys.add(row.keys[i]); 127 } 128 } 129 return keys == null ? Collections.<String> emptyList() : keys; 130 } 131 132 private static boolean same(Object a, Object b) { 133 if (a == null) { 134 return b == null; 135 } else { 136 return a.equals(b); 137 } 138 } 139 140 /** 141 * Comparator of {@link SimpleFragment}s according to a field. 142 */ 143 public static class FieldComparator implements Comparator<SimpleFragment> { 144 145 public final String key; 146 147 public FieldComparator(String key) { 148 this.key = key; 149 } 150 151 @Override 152 public int compare(SimpleFragment frag1, SimpleFragment frag2) { 153 return doCompare(frag1, frag2); 154 } 155 156 // separate function because we need a free generic type 157 // which is incompatible with the super signature 158 @SuppressWarnings("unchecked") 159 public <T> int doCompare(SimpleFragment frag1, SimpleFragment frag2) { 160 Comparable<T> value1 = (Comparable<T>) frag1.get(key); 161 T value2 = (T) frag2.get(key); 162 if (value1 == null && value2 == null) { 163 // coherent sort 164 return frag1.hashCode() - frag2.hashCode(); 165 } 166 if (value1 == null) { 167 return 1; 168 } 169 if (value2 == null) { 170 return -1; 171 } 172 return value1.compareTo(value2); 173 } 174 } 175 176}