Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9645ac22b9fa77972de1d4c4c7fe20e8f4313408 (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
/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.core.subscribers;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.variants.*;

/**
 * A <code>ResourceVariantByteStore</code> that optimizes the memory footprint
 * of a remote resource variant tree by only storing those bytes that differ
 * from a base resource variant tree. This class should only be used for cases
 * where the base and remote are on the same line-of-descent. For example, when
 * the remote tree represents the current state of a branch and the base
 * represents the state of the same branch when the local workspace as last
 * refreshed.
 * <p>
 * This class also contains the logic that allows subclasses to determine if
 * bytes stored in the remote tree are on a different line-of-descent than the
 * base. This is necessary because it is possible for the base tree to change in
 * ways that invalidate the stored remote variants. For example, if the local
 * resources are moved from the main trunk to a branch, any cached remote
 * resource variants would be stale.
 *
 * @since 3.0
 */
public abstract class DescendantResourceVariantByteStore extends ResourceVariantByteStore {

	ResourceVariantByteStore baseStore, remoteStore;

	public DescendantResourceVariantByteStore(ResourceVariantByteStore baseCache, ResourceVariantByteStore remoteCache) {
		this.baseStore = baseCache;
		this.remoteStore = remoteCache;
	}

	/**
	 * This method will dispose the remote cache but not the base cache.
	 * @see org.eclipse.team.core.variants.ResourceVariantByteStore#dispose()
	 */
	public void dispose() {
		remoteStore.dispose();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.team.internal.core.subscribers.caches.ResourceVariantByteStore#getBytes(org.eclipse.core.resources.IResource)
	 */
	public byte[] getBytes(IResource resource) throws TeamException {
		byte[] remoteBytes = remoteStore.getBytes(resource);
		byte[] baseBytes = baseStore.getBytes(resource);
		if (baseBytes == null) {
			// There is no base so use the remote bytes
			return remoteBytes;
		}
		if (remoteBytes == null) {
			if (isVariantKnown(resource)) {
				// The remote is known to not exist
				// TODO: The check for NO_REMOTE does not take into consideration the line-of-descent
				return remoteBytes;
			} else {
				// The remote was either never queried or was the same as the base.
				// In either of these cases, the base bytes are used.
				return baseBytes;
			}
		}
		if (isDescendant(resource, baseBytes, remoteBytes)) {
			// Only use the remote bytes if they are later on the same line-of-descent as the base
			return remoteBytes;
		}
		// Use the base sbytes since the remote bytes must be stale (i.e. are
		// not on the same line-of-descent
		return baseBytes;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.team.internal.core.subscribers.caches.ResourceVariantByteStore#setBytes(org.eclipse.core.resources.IResource, byte[])
	 */
	public boolean setBytes(IResource resource, byte[] bytes) throws TeamException {
		byte[] baseBytes = baseStore.getBytes(resource);
		if (baseBytes != null && equals(baseBytes, bytes)) {
			// Remove the existing bytes so the base will be used (thus saving space)
			return remoteStore.flushBytes(resource, IResource.DEPTH_ZERO);
		} else {
			return remoteStore.setBytes(resource, bytes);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.team.internal.core.subscribers.caches.ResourceVariantByteStore#removeBytes(org.eclipse.core.resources.IResource, int)
	 */
	public boolean flushBytes(IResource resource, int depth) throws TeamException {
		return remoteStore.flushBytes(resource, depth);
	}

	/**
	 * Return <code>true</code> if the variant associated with the given local
	 * resource has been cached. This method is useful for those cases when
	 * there are no bytes for a resource variant and the client wants to
	 * know if this means that the remote does exist (i.e. this method returns
	 * <code>true</code>) or the remote has not been fetched (i.e. this method returns
	 * <code>false</code>).
	 * @param resource the local resource
	 * @return <code>true</code> if the variant associated with the given local
	 * resource has been cached.
	 * @throws TeamException
	 */
	public abstract boolean isVariantKnown(IResource resource) throws TeamException;

	/**
	 * This method indicates whether the remote bytes are a later revision or version
	 * on the same line-of-descent as the base. A line of descent may be a branch or a fork
	 * (depending on the terminology used by the versioing server). If this method returns
	 * <code>false</code> then the remote bytes will be ignored by this tree.
	 * @param resource the local resource
	 * @param baseBytes the base bytes for the local resoource
	 * @param remoteBytes the remote bytes for the local resoource
	 * @return whether the remote bytes are later on the same line-of-descent as the base bytes
	 */
	protected abstract boolean isDescendant(IResource resource, byte[] baseBytes, byte[] remoteBytes) throws TeamException;

	/* (non-Javadoc)
	 * @see org.eclipse.team.internal.core.subscribers.caches.ResourceVariantByteStore#setVariantDoesNotExist(org.eclipse.core.resources.IResource)
	 */
	public boolean deleteBytes(IResource resource) throws TeamException {
		return remoteStore.deleteBytes(resource);
	}

	/**
	 * Return the base tree from which the remote is descendant.
	 * @return Returns the base tree.
	 */
	protected ResourceVariantByteStore getBaseStore() {
		return baseStore;
	}

	/**
	 * Return the remote tree which contains bytes only for the resource variants
	 * that differ from those in the base tree.
	 * @return Returns the remote tree.
	 */
	protected ResourceVariantByteStore getRemoteStore() {
		return remoteStore;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.team.internal.core.subscribers.caches.ResourceVariantByteStore#members(org.eclipse.core.resources.IResource)
	 */
	public IResource[] members(IResource resource) throws TeamException {
		IResource[] remoteMembers = getRemoteStore().members(resource);
		IResource[] baseMembers = getBaseStore().members(resource);
		Set members = new HashSet();
		for (int i = 0; i < remoteMembers.length; i++) {
			members.add(remoteMembers[i]);
		}
		for (int i = 0; i < baseMembers.length; i++) {
			IResource member = baseMembers[i];
			// Add the base only if the remote does not know about it
			// (i.e. hasn't marked it as deleted
			if (!isVariantKnown(member)) {
				members.add(member);
			}
		}
		return (IResource[]) members.toArray(new IResource[members.size()]);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.team.core.variants.ResourceVariantByteStore#run(org.eclipse.core.resources.IResource, org.eclipse.core.resources.IWorkspaceRunnable, org.eclipse.core.runtime.IProgressMonitor)
	 */
	public void run(IResource root, IWorkspaceRunnable runnable, IProgressMonitor monitor) throws TeamException {
		remoteStore.run(root, runnable, monitor);
	}
}

Back to the top