/* * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.storage.sql; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import org.nuxeo.ecm.core.storage.sql.RowMapper.RowUpdate; /** * 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() { 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() { row.size = 0; return State.ABSENT; } /** * Gets a value by key. * * @param key the key * @return the value */ public Serializable get(String key) { 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) { accessed(); // maybe refetch other values row.put(key, value, oldvalues); // pass oldvalues to be able to deal with deltas // resize oldvalues 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); } if (getState() != State.ABSENT || value != null) { // don't mark modified when setting null in an absent fragment // to avoid creating unneeded rows 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) { return (String) get(key); } @Override public RowUpdate getRowUpdate() { Collection<String> keys = getDirtyKeys(); if (keys.isEmpty()) { return null; } return new RowUpdate(row, keys); } /** * 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 a field. */ public static class FieldComparator implements Comparator<SimpleFragment> { public final String key; public FieldComparator(String key) { this.key = key; } @Override public int compare(SimpleFragment frag1, SimpleFragment frag2) { return doCompare(frag1, frag2); } // separate function because we need a free generic type // which is incompatible with the super signature @SuppressWarnings("unchecked") public <T> int doCompare(SimpleFragment frag1, SimpleFragment frag2) { Comparable<T> value1 = (Comparable<T>) frag1.get(key); T value2 = (T) frag2.get(key); if (value1 == null && value2 == null) { // coherent sort return frag1.hashCode() - frag2.hashCode(); } if (value1 == null) { return 1; } if (value2 == null) { return -1; } return value1.compareTo(value2); } } }