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