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 */
019package org.nuxeo.ecm.core.storage.sql;
020
021import java.io.Serializable;
022import java.util.HashSet;
023import java.util.Set;
024
025/**
026 * A set of invalidations.
027 * <p>
028 * Records both modified and deleted fragments, as well as "parents modified" fragments.
029 */
030public class Invalidations implements Serializable {
031
032    private static final long serialVersionUID = 1L;
033
034    /** Pseudo-table for children invalidation. */
035    public static final String PARENT = "__PARENT__";
036
037    /** Pseudo-table for series proxies invalidation. */
038    public static final String SERIES_PROXIES = "__SERIES_PROXIES__";
039
040    /** Pseudo-table for target proxies invalidation. */
041    public static final String TARGET_PROXIES = "__TARGET_PROXIES__";
042
043    public static final int MODIFIED = 1;
044
045    public static final int DELETED = 2;
046
047    /**
048     * Maximum number of invalidations kept, after which only {@link #all} is set. This avoids accumulating too many
049     * invalidations in memory, at the expense of more coarse-grained invalidations.
050     */
051    public static final int MAX_SIZE = 10000;
052
053    /**
054     * Used locally when invalidating everything, or when too many invalidations have been received.
055     */
056    public boolean all;
057
058    /** null when empty */
059    public Set<RowId> modified;
060
061    /** null when empty */
062    public Set<RowId> deleted;
063
064    public Invalidations() {
065    }
066
067    public Invalidations(boolean all) {
068        this.all = all;
069    }
070
071    public boolean isEmpty() {
072        return modified == null && deleted == null && !all;
073    }
074
075    public void clear() {
076        all = false;
077        modified = null;
078        deleted = null;
079    }
080
081    protected void setAll() {
082        all = true;
083        modified = null;
084        deleted = null;
085    }
086
087    protected void checkMaxSize() {
088        if (modified != null && modified.size() > MAX_SIZE //
089                || deleted != null && deleted.size() > MAX_SIZE) {
090            setAll();
091        }
092    }
093
094    /** only call this if it's to add at least one element in the set */
095    public Set<RowId> getKindSet(int kind) {
096        switch (kind) {
097        case MODIFIED:
098            if (modified == null) {
099                modified = new HashSet<RowId>();
100            }
101            return modified;
102        case DELETED:
103            if (deleted == null) {
104                deleted = new HashSet<RowId>();
105            }
106            return deleted;
107        }
108        throw new AssertionError();
109    }
110
111    public void add(Invalidations other) {
112        if (other == null) {
113            return;
114        }
115        if (all) {
116            return;
117        }
118        if (other.all) {
119            setAll();
120            return;
121        }
122        if (other.modified != null) {
123            if (modified == null) {
124                modified = new HashSet<RowId>();
125            }
126            modified.addAll(other.modified);
127        }
128        if (other.deleted != null) {
129            if (deleted == null) {
130                deleted = new HashSet<RowId>();
131            }
132            deleted.addAll(other.deleted);
133        }
134        checkMaxSize();
135    }
136
137    public void addModified(RowId rowId) {
138        if (all) {
139            return;
140        }
141        if (modified == null) {
142            modified = new HashSet<RowId>();
143        }
144        modified.add(rowId);
145        checkMaxSize();
146    }
147
148    public void addDeleted(RowId rowId) {
149        if (all) {
150            return;
151        }
152        if (deleted == null) {
153            deleted = new HashSet<RowId>();
154        }
155        deleted.add(rowId);
156        checkMaxSize();
157    }
158
159    public void add(Serializable id, String[] tableNames, int kind) {
160        if (tableNames.length == 0) {
161            return;
162        }
163        Set<RowId> set = getKindSet(kind);
164        for (String tableName : tableNames) {
165            set.add(new RowId(tableName, id));
166        }
167        checkMaxSize();
168    }
169
170    @Override
171    public String toString() {
172        StringBuilder sb = new StringBuilder(this.getClass().getSimpleName() + '(');
173        if (all) {
174            sb.append("all=true");
175        }
176        if (modified != null) {
177            sb.append("modified=");
178            sb.append(modified);
179            if (deleted != null) {
180                sb.append(',');
181            }
182        }
183        if (deleted != null) {
184            sb.append("deleted=");
185            sb.append(deleted);
186        }
187        sb.append(')');
188        return sb.toString();
189    }
190
191}