File: PluggableList.java

package info (click to toggle)
libglazedlists-java 1.9.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,024 kB
  • ctags: 4,252
  • sloc: java: 22,561; xml: 818; sh: 51; makefile: 5
file content (161 lines) | stat: -rw-r--r-- 7,034 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists;

import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventPublisher;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;

/**
 * An {@link EventList} which delegates all List methods to a given source
 * {@link EventList} that may be replaced at runtime using
 * {@link #setSource(EventList)}.
 *
 * <p>Note that the source {@link EventList} must use the same
 * {@link ListEventPublisher} and {@link ReadWriteLock}, particularly if this
 * {@link EventList} is to be used by multiple threads concurrently. To
 * construct an {@link EventList} that shares the {@link ListEventPublisher}
 * and {@link ReadWriteLock} with this {@link PluggableList}, use
 * {@link #createSourceList()}.
 *
 * <p><strong><font color="#FF0000">Warning:</font></strong> This class is
 * thread ready but not thread safe. See {@link EventList} for an example
 * of thread safe code.
 *
 * <p><table border="1" width="100%" cellpadding="3" cellspacing="0">
 * <tr class="TableHeadingColor"><td colspan=2><font size="+2"><b>EventList Overview</b></font></td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Writable:</b></td><td>yes</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Concurrency:</b></td><td><strong>only {@link #setSource(EventList)}</strong></td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Performance:</b></td><td>delegates to source EventList</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Memory:</b></td><td>N/A</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Unit Tests:</b></td><td>N/A</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Issues:</b></td><td></td></tr>
 * </table>
 *
 * @author James Lemieux
 */
public class PluggableList<E> extends TransformedList<E, E> {

    /**
     * Constructs a PluggableList which uses the given <code>publisher</code>
     * and <code>lock</code>. The PluggableList will default to use a
     * {@link BasicEventList} that also uses the same <code>publisher</code>
     * and <code>lock</code>.
     *
     * @param publisher the {@link ListEventPublisher} to use within the {@link PluggableList}
     * @param lock the {@link ReadWriteLock} to use within the {@link PluggableList}
     */
    public PluggableList(ListEventPublisher publisher, ReadWriteLock lock) {
        this(new BasicEventList<E>(publisher, lock));
    }

    /**
     * Constructs a PluggableList which delegates all List methods to the given
     * <code>source</code>. At some future time, the source EventList may be
     * replaced using {@link #setSource(EventList)} and this PluggableList will
     * produce a {@link ListEvent} describing the change in data.
     *
     * @param source the source of data to this PluggableList
     */
    public PluggableList(EventList<E> source) {
        super(source);
        source.addListEventListener(this);
    }

    /**
     * Creates a new {@link EventList} that shares its
     * {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and
     * {@link ca.odell.glazedlists.event.ListEventPublisher} with this
     * {@link PluggableList}. This is necessary when this {@link PluggableList}
     * will be used by multiple threads.
     *
     * <p>Note that the created {@link EventList} must be explicitly set as the
     * source of this {@link PluggableList} using {@link #setSource(EventList)}.
     *
     * @return a new EventList appropriate for use as the
     *      {@link #setSource(EventList) source} of this PluggableList
     */
    public EventList<E> createSourceList() {
        return new BasicEventList<E>(getPublisher(), getReadWriteLock());
    }

    /**
     * Sets the source EventList to which this PluggableList will delegate all
     * calls. This method is the entire reason that PluggableList exists. It
     * allows the data source of the remaining pipeline to be altered.
     * <p>
     * To ensure correct behaviour when this {@link PluggableList} is used by
     * multiple threads, the given <code>source</code> <strong>must</strong>
     * share the same {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and
     * {@link ca.odell.glazedlists.event.ListEventPublisher} with this PluggableList.
     *
     * @param source the new source of data for this PluggableList, and all
     *      downstream EventLists
     * @throws IllegalStateException if this PluggableList is already disposed
     * @throws IllegalArgumentException if any of the following are true
     *         <ul>
     *           <li>the given source is null</li>
     *           <li>the given source has a different ListEventPublisher than this PluggableList</li>
     *           <li>the given source has a different ReadWriteLock than this PluggableList</li>
     *         </ul>
     */
    public void setSource(EventList<E> source) {
        // lock the pipeline while the source list is swapped
        getReadWriteLock().writeLock().lock();
        try {
            if (this.source == null)
                throw new IllegalStateException("setSource may not be called on a disposed PluggableList");

            if (source == null)
                throw new IllegalArgumentException("source may not be null");

            if (!getReadWriteLock().equals(source.getReadWriteLock()))
                throw new IllegalArgumentException("source list must share lock with PluggableList");

            if (!getPublisher().equals(source.getPublisher()))
                throw new IllegalArgumentException("source list must share publisher with PluggableList");

            if (this.source == source)
                return;
            
            updates.beginEvent();
            // add deletions to the ListEvent for all the elements in the old source
            for (int i = 0, n = size(); i < n; i++)
                updates.elementDeleted(0, get(i));

            this.source.removeListEventListener(this);
            this.source = source;
            this.source.addListEventListener(this);

            // add insertions to the ListEvent for all the elements in the new source
            for (int i = 0, n = size(); i < n; i++)
                updates.elementInserted(i, get(i));

            // broadcast the ListEvent that describes the data change
            updates.commitEvent();
        } finally {
            getReadWriteLock().writeLock().unlock();
        }
    }

    /** @inheritDoc */
    @Override
    protected boolean isWritable() {
        return true;
    }

    /** @inheritDoc */
    @Override
    public void listChanged(ListEvent<E> listChanges) {
        updates.forwardEvent(listChanges);
    }

    /** @inheritDoc */
    @Override
    public void dispose() {
        if (source != null)
            source.removeListEventListener(this);
        source = null;
    }
}