File: BlockSequence.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 (270 lines) | stat: -rw-r--r-- 8,966 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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.event;

import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.impl.adt.IntArrayList;

import java.util.ArrayList;
import java.util.List;

/**
 * Manage a very simple list of list event blocks that occur in
 * increasing-only order.
 *
 * @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
 */
public class BlockSequence<E> {

    /** the start indices of the change blocks, inclusive */
    private IntArrayList starts = new IntArrayList();
    /** the end indices of the change blocks, exclusive */
    private IntArrayList ends = new IntArrayList();
    /** the change types */
    private IntArrayList types = new IntArrayList();
    /** the impacted values */
    private List<E> oldValues = new ArrayList<E>();
    private List<E> newValues = new ArrayList<E>();

    /**
     * @param startIndex the first updated element, inclusive
     * @param endIndex the last index, exclusive
     */
    public boolean update(int startIndex, int endIndex) {
        return addChange(ListEvent.UPDATE, startIndex, endIndex, ListEvent.<E>unknownValue(), ListEvent.<E>unknownValue());
    }

    /**
     * @param startIndex the first inserted element, inclusive
     * @param endIndex the last index, exclusive
     */
    public boolean insert(int startIndex, int endIndex) {
        return addChange(ListEvent.INSERT, startIndex, endIndex, ListEvent.<E>unknownValue(), ListEvent.<E>unknownValue());
    }

    /**
     * @param startIndex the index of the first element to remove
     * @param endIndex the last index, exclusive
     */
    public boolean delete(int startIndex, int endIndex) {
        return addChange(ListEvent.DELETE, startIndex, endIndex, ListEvent.<E>unknownValue(), ListEvent.<E>unknownValue());
    }

    /**
     * Add this change to the list, or return <code>false</code> if that failed
     * because the change is not in increasing order.
     *
     * @return true if the change was successfully applied, or <code>false</code>
     *      if no change was made because this change could not be handled.
     */
    public boolean addChange(int type, int startIndex, int endIndex, E oldValue, E newValue) {
        // remind ourselves of the most recent change
        int lastType;
        int lastStartIndex;
        int lastEndIndex;
        int lastChangedIndex;
        int size = types.size();
        E lastOldValue;
        E lastNewValue;
        if(size == 0) {
            lastType = -1;
            lastStartIndex = -1;
            lastEndIndex = 0;
            lastChangedIndex = 0;
            lastOldValue = ListEvent.<E>unknownValue();
            lastNewValue = ListEvent.<E>unknownValue();
        } else {
            lastType = types.get(size - 1);
            lastStartIndex = starts.get(size - 1);
            lastEndIndex = ends.get(size - 1);
            lastChangedIndex = (lastType == ListEvent.DELETE) ? lastStartIndex : lastEndIndex;
            lastOldValue = (lastType == ListEvent.DELETE) ? oldValues.get(size - 1) : ListEvent.<E>unknownValue();
            lastNewValue = newValues.get(size - 1);
        }

        // this change breaks the linear-ordering requirement, convert
        // to a more powerful list blocks manager
        if(startIndex < lastChangedIndex) {
            return false;

        // concatenate this change on to the previous one
        } else if(lastChangedIndex == startIndex && lastType == type && oldValue == lastOldValue && newValue == lastNewValue) {
            int newLength = (lastEndIndex - lastStartIndex) + (endIndex - startIndex);
            ends.set(size - 1, lastStartIndex + newLength);
            return true;

        // add this change to the end of the list
        } else {
            starts.add(startIndex);
            ends.add(endIndex);
            types.add(type);
            oldValues.add(oldValue);
            newValues.add(newValue);
            return true;
        }
    }

    public boolean isEmpty() {
        return types.isEmpty();
    }

    public void reset() {
        starts.clear();
        ends.clear();
        types.clear();
        oldValues.clear();
        newValues.clear();
    }

    public Iterator iterator() {
        return new Iterator();
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        StringBuffer result = new StringBuffer();
        for(int i = 0; i < types.size(); i++) {
            if(i != 0) {
                result.append(", ");
            }

            // write the type
            int type = types.get(i);
            if(type == ListEvent.INSERT) result.append("+");
            else if(type == ListEvent.UPDATE) result.append("U");
            else if(type == ListEvent.DELETE) result.append("X");

            // write the range
            int start = starts.get(i);
            int end = ends.get(i);
            result.append(start);
            if(end != start) {
                result.append("-");
                result.append(end);
            }
        }

        return result.toString();
    }

    /**
     * Iterate through the list of changes in this sequence.
     */
    public class Iterator {

        private int blockIndex = -1;
        private int offset = 0;

        private int startIndex = -1;
        private int endIndex = -1;
        private int type = -1;

        public Iterator copy() {
            Iterator result = new Iterator();
            result.blockIndex = blockIndex;
            result.offset = offset;
            result.startIndex = startIndex;
            result.endIndex = endIndex;
            result.type = type;
            return result;
        }

        public int getIndex() {
            if(type == ListEvent.INSERT || type == ListEvent.UPDATE) {
                return startIndex + offset;
            } else if(type == ListEvent.DELETE) {
                return startIndex;
            } else {
                throw new IllegalStateException();
            }
        }
        public int getBlockStart() {
            if(startIndex == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a block start index");
            return startIndex;
        }
        public int getBlockEnd() {
            if(endIndex == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a block end index");
            return endIndex;
        }
        public int getType() {
            if(type == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a type");
            return type;
        }
        public E getOldValue() {
            return oldValues.get(blockIndex);
        }
        public E getNewValue() {
            return newValues.get(blockIndex);
        }

        /**
         * Move to the next changed index, possibly within the same block.
         */
        public boolean next() {
            // increment within the block
            if(offset + 1 < endIndex - startIndex) {
                offset++;
                return true;

            // increment to the next block
            } else if(blockIndex + 1 < types.size()) {
                blockIndex++;
                offset = 0;
                startIndex = starts.get(blockIndex);
                endIndex = ends.get(blockIndex);
                type = types.get(blockIndex);
                return true;

            // no more left
            } else {
                return false;
            }
        }

        /**
         * Move to the next changed block.
         */
        public boolean nextBlock() {
            // increment to the next block
            if(blockIndex + 1 < types.size()) {
                blockIndex++;
                offset = 0;
                startIndex = starts.get(blockIndex);
                endIndex = ends.get(blockIndex);
                type = types.get(blockIndex);
                return true;

            // no more left
            } else {
                return false;
            }
        }

        /**
         * @return true if theres another changed index
         */
        public boolean hasNext() {
            // increment within the block
            if(offset + 1 < endIndex - startIndex) return true;

            // increment to the next block
            if(blockIndex + 1 < types.size()) return true;

            // no more left
            return false;
        }

        /**
         * @return true if theres another changed block
         */
        public boolean hasNextBlock() {
            // increment to the next block
            if(blockIndex + 1 < types.size()) return true;

            // no more left
            return false;
        }
    }
}