Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: efc414acbbccd997776ff85ecb4f6510bb384ea6 (plain) (blame)
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
/*******************************************************************************
 * Copyright (c) 2007, 2012 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.tcf.debug.ui.ITCFChildren;
import org.eclipse.tcf.debug.ui.ITCFObject;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.util.TCFDataCache;

/**
 * TCFChildren is a concrete type of TCF data cache that is used to cache a list of children.
 */
public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> implements ITCFChildren {

    private final int pool_margin;
    private final Map<String,TCFNode> node_pool = new LinkedHashMap<String,TCFNode>(32, 0.75f, true);

    protected final TCFNode node;

    private static final TCFNode[] EMPTY_NODE_ARRAY = new TCFNode[0];

    private TCFNode[] array;
    private Map<String,ITCFObject> obj_map;

    TCFChildren(TCFNode node) {
        super(node.channel);
        this.node = node;
        pool_margin = 0;
        node.addDataCache(this);
    }

    TCFChildren(TCFNode node, int pool_margin) {
        super(node.channel);
        this.node = node;
        this.pool_margin = pool_margin;
        node.addDataCache(this);
    }

    /**
     * Dispose the cache and all nodes in the nodes pool.
     */
    @Override
    public void dispose() {
        assert !isDisposed();
        node.removeDataCache(this);
        for (TCFNode n : node_pool.values()) n.dispose();
        node_pool.clear();
        super.dispose();
    }

    /**
     * Remove a node from cache.
     * The method is called every time a node is disposed.
     * @param id - node ID
     */
    void onNodeDisposed(String id) {
        node_pool.remove(id);
        if (isValid()) {
            array = null;
            obj_map = null;
            Map<String,TCFNode> data = getData();
            if (data != null) data.remove(id);
        }
    }

    private void addToPool(Map<String,TCFNode> data) {
        assert !isDisposed();
        for (TCFNode n : data.values()) {
            assert data.get(n.id) == n;
            assert n.parent == node;
            node_pool.put(n.id, n);
        }
        if (node_pool.size() > data.size() + pool_margin) {
            String[] arr = node_pool.keySet().toArray(new String[node_pool.size()]);
            for (String id : arr) {
                if (data.get(id) == null) {
                    node_pool.get(id).dispose();
                    if (node_pool.size() <= data.size() + pool_margin) break;
                }
            }
        }
    }

    /**
     * End cache pending state.
     * @param token - pending command handle.
     * @param error - data retrieval error or null
     * @param data - up-to-date map of children nodes
     */
    @Override
    public void set(IToken token, Throwable error, Map<String,TCFNode> data) {
        array = null;
        obj_map = null;
        if (isDisposed()) {
            // A command can return data after the cache element has been disposed.
            // Just ignore the data in such case.
            super.set(token, null, null);
            assert node_pool.isEmpty();
        }
        else if (data != null) {
            super.set(token, error, data);
            addToPool(data);
        }
        else {
            super.set(token, error, new HashMap<String,TCFNode>());
        }
    }

    /**
     * Set given data to the cache, mark cache as valid, cancel any pending data retrieval.
     * @param data - up-to-date data to store in the cache, null means empty collection of nodes.
     */
    @Override
    public void reset(Map<String,TCFNode> data) {
        assert !isDisposed();
        array = null;
        obj_map = null;
        if (data != null) {
            super.reset(data);
            addToPool(data);
        }
        else {
            super.reset(new HashMap<String,TCFNode>());
        }
    }

    /**
     * Invalidate the cache. If retrieval is in progress - let it continue.
     */
    @Override
    public void reset() {
        super.reset();
        array = null;
        obj_map = null;
    }

    /**
     * Force cache to invalid state, cancel pending data retrieval if any.
     */
    @Override
    public void cancel() {
        super.cancel();
        array = null;
        obj_map = null;
    }

    /**
     * Add a node to collection of children.
     * @param n - a node.
     */
    void add(TCFNode n) {
        assert !isDisposed();
        assert !n.isDisposed();
        assert node_pool.get(n.id) == null;
        assert n.parent == node;
        node_pool.put(n.id, n);
        if (isValid()) {
            array = null;
            obj_map = null;
            Map<String,TCFNode> data = getData();
            if (data != null) data.put(n.id, n);
        }
    }

    /**
     * Return collection of all nodes, including current children as well as
     * currently unused nodes from the pool.
     * To get only current children use getData() method.
     * @return Collection of nodes.
     */
    Collection<TCFNode> getNodes() {
        return node_pool.values();
    }

    /**
     * Return current number of children.
     * The cache must be valid for the method to work.
     * @return number of children.
     */
    public int size() {
        assert isValid();
        Map<String,TCFNode> data = getData();
        return data == null ? 0 : data.size();
    }

    /**
     * Return current children nodes as a sorted array.
     * @return array of nodes.
     */
    public TCFNode[] toArray() {
        assert isValid();
        if (array != null) return array;
        Map<String,TCFNode> data = getData();
        if (data == null || data.size() == 0) return array = EMPTY_NODE_ARRAY;
        array = data.values().toArray(new TCFNode[data.size()]);
        Arrays.sort(array);
        return array;
    }

    /**
     * Return current children nodes as a map of ITCFObject.
     * @return map of ITCFObject.
     */
    public Map<String,ITCFObject> getChildren() {
        assert isValid();
        if (obj_map != null) return obj_map;
        Map<String,TCFNode> data = getData();
        obj_map = new HashMap<String,ITCFObject>();
        if (data == null) return obj_map;
        for (TCFNode n : data.values()) obj_map.put(n.id, n);
        return obj_map;
    }

    /**
     * Return current children nodes in IChildrenUpdate object.
     * @param update - children update request object.
     * @param done - a call-back object, it is called when cache state changes.
     * @return true if all done, false if data request is pending.
     */
    boolean getData(IChildrenUpdate update, Runnable done) {
        if (!validate(done)) return false;
        TCFNode[] arr = toArray();
        int offset = 0;
        int r_offset = update.getOffset();
        int r_length = update.getLength();
        for (TCFNode n : arr) {
            if (offset >= r_offset && offset < r_offset + r_length) {
                update.setChild(n, offset);
            }
            offset++;
        }
        return true;
    }
}

Back to the top