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.Collection;
024import java.util.Collections;
025import java.util.Comparator;
026import java.util.LinkedList;
027import java.util.List;
028
029import org.nuxeo.ecm.core.storage.sql.RowMapper.RowUpdate;
030
031/**
032 * A type of fragment corresponding to a single row in a table and its associated in-memory information (state, dirty
033 * fields, attached context).
034 */
035public final class SimpleFragment extends Fragment {
036
037    private static final long serialVersionUID = 1L;
038
039    private static final Row UNKNOWN_ROW = new Row(null, (Serializable) null);
040
041    public static final SimpleFragment UNKNOWN = new SimpleFragment(UNKNOWN_ROW, State.DETACHED, null);
042
043    /**
044     * Constructs a {@link SimpleFragment} from a {@link Row}.
045     *
046     * @param row the row, or {@code null}
047     * @param state the initial state for the fragment
048     * @param context the persistence context to which the fragment is tied, or {@code null}
049     */
050    public SimpleFragment(Row row, State state, PersistenceContext context) {
051        super(row, state, context);
052    }
053
054    @Override
055    protected State refetch() {
056        Row newrow = context.mapper.readSimpleRow(row);
057        if (newrow == null) {
058            row.size = 0;
059            return State.ABSENT;
060        } else {
061            row = newrow;
062            clearDirty();
063            return State.PRISTINE;
064        }
065    }
066
067    @Override
068    protected State refetchDeleted() {
069        row.size = 0;
070        return State.ABSENT;
071    }
072
073    /**
074     * Gets a value by key.
075     *
076     * @param key the key
077     * @return the value
078     */
079    public Serializable get(String key) {
080        accessed();
081        return row.get(key);
082    }
083
084    /**
085     * Puts a value by key.
086     *
087     * @param key the key
088     * @param value the value
089     */
090    public void put(String key, Serializable value) {
091        accessed(); // maybe refetch other values
092        row.put(key, value, oldvalues); // pass oldvalues to be able to deal with deltas
093        // resize oldvalues to follow row if needed
094        if (oldvalues.length < row.values.length) {
095            Serializable[] tmp = oldvalues;
096            oldvalues = new Serializable[row.values.length];
097            System.arraycopy(tmp, 0, oldvalues, 0, tmp.length);
098        }
099        if (getState() != State.ABSENT || value != null) {
100            // don't mark modified when setting null in an absent fragment
101            // to avoid creating unneeded rows
102            markModified();
103        }
104    }
105
106    /**
107     * Returns a {@code String} value.
108     *
109     * @param key the key
110     * @return the value as a {@code String}
111     * @throws ClassCastException if the value is not a {@code String}
112     */
113    public String getString(String key) {
114        return (String) get(key);
115    }
116
117    @Override
118    public RowUpdate getRowUpdate() {
119        Collection<String> keys = getDirtyKeys();
120        if (keys.isEmpty()) {
121            return null;
122        }
123        return new RowUpdate(row, keys);
124    }
125
126    /**
127     * Gets the dirty keys (keys of values changed since last clear).
128     *
129     * @return the dirty keys
130     */
131    public List<String> getDirtyKeys() {
132        List<String> keys = null;
133        for (int i = 0; i < row.size; i++) {
134            if (!same(oldvalues[i], row.values[i])) {
135                if (keys == null) {
136                    keys = new LinkedList<String>();
137                }
138                keys.add(row.keys[i]);
139            }
140        }
141        return keys == null ? Collections.<String> emptyList() : keys;
142    }
143
144    private static boolean same(Object a, Object b) {
145        if (a == null) {
146            return b == null;
147        } else {
148            return a.equals(b);
149        }
150    }
151
152    /**
153     * Comparator of {@link SimpleFragment}s according to a field.
154     */
155    public static class FieldComparator implements Comparator<SimpleFragment> {
156
157        public final String key;
158
159        public FieldComparator(String key) {
160            this.key = key;
161        }
162
163        @Override
164        public int compare(SimpleFragment frag1, SimpleFragment frag2) {
165            return doCompare(frag1, frag2);
166        }
167
168        // separate function because we need a free generic type
169        // which is incompatible with the super signature
170        @SuppressWarnings("unchecked")
171        public <T> int doCompare(SimpleFragment frag1, SimpleFragment frag2) {
172            Comparable<T> value1 = (Comparable<T>) frag1.get(key);
173            T value2 = (T) frag2.get(key);
174            if (value1 == null && value2 == null) {
175                // coherent sort
176                return frag1.hashCode() - frag2.hashCode();
177            }
178            if (value1 == null) {
179                return 1;
180            }
181            if (value2 == null) {
182                return -1;
183            }
184            return value1.compareTo(value2);
185        }
186    }
187
188}