/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.ecr.core.storage.StorageException;
/**
* A type of fragment corresponding to a single row in a table and its
* associated in-memory information (state, dirty fields, attached context).
*/
public final class SimpleFragment extends Fragment {
private static final long serialVersionUID = 1L;
private static final Row UNKNOWN_ROW = new Row(null, (Serializable) null);
public static final SimpleFragment UNKNOWN = new SimpleFragment(
UNKNOWN_ROW, State.DETACHED, null);
/**
* Constructs a {@link SimpleFragment} from a {@link Row}.
*
* @param row the row, or {@code null}
* @param state the initial state for the fragment
* @param context the persistence context to which the fragment is tied, or
* {@code null}
*/
public SimpleFragment(Row row, State state, PersistenceContext context) {
super(row, state, context);
}
@Override
protected State refetch() throws StorageException {
Row newrow = context.mapper.readSimpleRow(row);
if (newrow == null) {
row.size = 0;
return State.ABSENT;
} else {
row = newrow;
clearDirty();
return State.PRISTINE;
}
}
@Override
protected State refetchDeleted() throws StorageException {
row.size = 0;
return State.ABSENT;
}
/**
* Gets a value by key.
*
* @param key the key
* @return the value
*/
public Serializable get(String key) throws StorageException {
accessed();
return row.get(key);
}
/**
* Puts a value by key.
*
* @param key the key
* @param value the value
*/
public void put(String key, Serializable value) throws StorageException {
accessed(); // maybe refetch other values
row.put(key, value);
// resize olddata to follow row if needed
if (oldvalues.length < row.values.length) {
Serializable[] tmp = oldvalues;
oldvalues = new Serializable[row.values.length];
System.arraycopy(tmp, 0, oldvalues, 0, tmp.length);
}
markModified();
}
/**
* Returns a {@code String} value.
*
* @param key the key
* @return the value as a {@code String}
* @throws ClassCastException if the value is not a {@code String}
*/
public String getString(String key) throws StorageException {
return (String) get(key);
}
/**
* Gets the dirty keys (keys of values changed since last clear).
*
* @return the dirty keys
*/
public List<String> getDirtyKeys() {
List<String> keys = null;
for (int i = 0; i < row.size; i++) {
if (!same(oldvalues[i], row.values[i])) {
if (keys == null) {
keys = new LinkedList<String>();
}
keys.add(row.keys[i]);
}
}
return keys == null ? Collections.<String> emptyList() : keys;
}
private static boolean same(Object a, Object b) {
if (a == null) {
return b == null;
} else {
return a.equals(b);
}
}
/**
* Comparator of {@link SimpleFragment}s according to their pos field.
*/
public static class PositionComparator implements
Comparator<SimpleFragment> {
protected final String posKey;
public PositionComparator(String posKey) {
this.posKey = posKey;
}
@Override
public int compare(SimpleFragment frag1, SimpleFragment frag2) {
try {
Long pos1 = (Long) frag1.get(posKey);
Long pos2 = (Long) frag2.get(posKey);
if (pos1 == null && pos2 == null) {
// coherent sort
return frag1.hashCode() - frag2.hashCode();
}
if (pos1 == null) {
return 1;
}
if (pos2 == null) {
return -1;
}
return pos1.compareTo(pos2);
} catch (StorageException e) {
// shouldn't happen
return frag1.hashCode() - frag2.hashCode();
}
}
}
}