Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 37351b7a9f22712c589a426f2e6893693f008efa (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
/*******************************************************************************
 * Copyright (c) 2006, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.core.mapping;

import java.io.IOException;
import java.io.OutputStream;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.content.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.*;
import org.eclipse.team.internal.core.*;
import org.eclipse.team.internal.core.mapping.IStreamMergerDelegate;

/**
 * This storage merger delegates to the appropriate merger or returns a conflict
 * if no merger is available or if a merge was not possible.
 * <p>
 * The target storage is used to look for an appropriate merger. If the target
 * is an {@link IFile}, the content type of the file is used. Otherwise, the
 * {@link IContentTypeManager} is used to find an appropriate content type. If an
 * appropriate merger is not found, a status containing the
 * <code>CONFLICT</code> is returned.
 * <p>
 * Clients may use this class directly or subclass it.
 * @since 3.4
 *
 */
public class DelegatingStorageMerger implements IStorageMerger {

	private static DelegatingStorageMerger instance;

	/**
	 * Return the storage merger associated with the <code>IContentTypeManager.CT_TEXT</code>
	 * content type.
	 * @return the storage merger associated with the <code>IContentTypeManager.CT_TEXT</code>
	 * content type
	 */
	public static IStorageMerger createTextMerger() {
		return Team.createMerger(Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT));
	}

	/**
	 * Default no-arg constructor.
	 */
	public DelegatingStorageMerger() {
		// Nothing to do
	}

	/**
	 * Helper method that returns a singleton instance that can be used to merge
	 * two {@link IStorage} instances.
	 * @return a storage merger that delegates the merge based on the type
	 * of the target storage.
	 */
	public static IStorageMerger getInstance() {
		if (instance == null)
			instance = new DelegatingStorageMerger();
		return instance;
	}

	@Override
	public IStatus merge(OutputStream output, String outputEncoding,
			IStorage ancestor, IStorage target, IStorage other,
			IProgressMonitor monitor) throws CoreException {
		IStorageMerger merger = createDelegateMerger(target);
		if (merger == null)
			return new Status(IStatus.WARNING, TeamPlugin.ID, CONFLICT,
					Messages.DelegatingStorageMerger_0, null);
		if (ancestor == null && !merger.canMergeWithoutAncestor()) {
			return new Status(IStatus.WARNING, TeamPlugin.ID, CONFLICT,
					NLS.bind(Messages.MergeContext_1, new String[] { target.getFullPath().toString() }), null);
		}
		return merger.merge(output, outputEncoding, ancestor, target, other, monitor);
	}

	/**
	 * Create a merger for the given storage or return <code>null</code>
	 * if an appropriate merger could not be created. This method is called
	 * by {@link #merge(OutputStream, String, IStorage, IStorage, IStorage, IProgressMonitor)}
	 * to create the merger to which the merge should be delegated.
	 * @param target the storage that contains the target contents of the merge.
	 * @return a merger for the given storage or <code>null</code>
	 * @throws CoreException
	 */
	protected IStorageMerger createDelegateMerger(IStorage target) throws CoreException {
		IStorageMerger merger = null;
		CoreException exception = null;
		try {
			IContentType type = getContentType(target);
			if (type != null)
				merger = getMerger(type);
		} catch (CoreException e) {
			exception = e;
		}
		// If an exception occurred trying to find a content type,
		// try using the extension before failing
		if (merger == null) {
			merger = getMerger(target.getName());
			if (merger == null) {
				// If team thinks the file is text, try to get a text merger for the file
				int type = getType(target);
				if (type == Team.TEXT)
					merger = createTextMerger();
				if (merger == null) {
					// As a last resort, look for a stream merger
					merger = findAndWrapStreamMerger(target);
				}
			}
		}
		if (exception != null) {
			if (merger == null) {
				// No merger was found so report the error
				throw exception;
			} else {
				// If an extension based merger was found, log the error
				TeamPlugin.log(exception);
			}
		}
		return merger;
	}

	/**
	 * Return the Team content type associated with the given
	 * target.
	 * @param target the storage that contains the target contents for the merge.
	 * @return the Team content type associated with the given
	 * target
	 * @see Team#getFileContentManager()
	 * @see IFileContentManager#getType(IStorage)
	 */
	protected int getType(IStorage target) {
		return Team.getFileContentManager().getType(target);
	}

	private IStorageMerger findAndWrapStreamMerger(IStorage target) {
		IStreamMergerDelegate mergerDelegate = TeamPlugin.getPlugin().getMergerDelegate();
		if (mergerDelegate != null) {
			IStorageMerger merger = mergerDelegate.findMerger(target);
			return merger;
		}
		return null;
	}

	private IStorageMerger getMerger(String name) {
		String extension = getExtension(name);
		if (extension != null)
			return StorageMergerRegistry.getInstance().createStreamMerger(extension);
		return null;
	}

	/**
	 * Helper method for returning the extension of a file name
	 * @param name the file name
	 * @return the extension of the file name or <code>null</code>
	 * if the file name does not have an extension
	 */
	public static String getExtension(String name) {
		int index = name.lastIndexOf('.');
		if (index == -1) {
			return null;
		}
		return name.substring(index + 1);
	}

	private IStorageMerger getMerger(IContentType type) {
		return Team.createMerger(type);
	}

	/**
	 * A helper method that finds the content type for the given storage or returns
	 * <code>null</code> if a content
	 * type cannot be found. Any exceptions that occur when trying to determine
	 * the content type are propagated.
	 * @param target the storage that contains the target contents of the merge.
	 * @return the content type of the storage or <code>null</code>
	 * @throws CoreException if an exception occurs
	 */
	public static IContentType getContentType(IStorage target) throws CoreException {
		if (target instanceof IFile) {
			IFile file = (IFile) target;
			IContentDescription contentDescription = file.getContentDescription();
			if (contentDescription != null) {
				IContentType contentType = contentDescription.getContentType();
				return contentType;
			}
		} else {
			IContentTypeManager manager = Platform.getContentTypeManager();
			try {
				IContentType type = manager.findContentTypeFor(target
						.getContents(), target.getName());
				return type;
			} catch (IOException e) {
				String name = target.getName();
				if (target.getFullPath() != null) {
					name = target.getFullPath().toString();
				}
				throw new TeamException(new Status(
					IStatus.ERROR,
					TeamPlugin.ID,
					INTERNAL_ERROR,
					NLS.bind(Messages.DelegatingStorageMerger_1,name), e));
			}
		}
		return null;
	}

	@Override
	public boolean canMergeWithoutAncestor() {
		return false;
	}

}

Back to the top