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