View Javadoc
1   /*
2    * Copyright (C) 2008, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.patch;
45  
46  import static org.eclipse.jgit.util.RawParseUtils.nextLF;
47  import static org.eclipse.jgit.util.RawParseUtils.parseBase10;
48  
49  import java.io.IOException;
50  import java.io.OutputStream;
51  import java.text.MessageFormat;
52  
53  import org.eclipse.jgit.internal.JGitText;
54  import org.eclipse.jgit.lib.AbbreviatedObjectId;
55  import org.eclipse.jgit.util.MutableInteger;
56  
57  /**
58   * Hunk header for a hunk appearing in a "diff --cc" style patch.
59   */
60  public class CombinedHunkHeader extends HunkHeader {
61  	private static abstract class CombinedOldImage extends OldImage {
62  		int nContext;
63  	}
64  
65  	private CombinedOldImage[] old;
66  
67  	CombinedHunkHeader(CombinedFileHeader fh, int offset) {
68  		super(fh, offset, null);
69  		old = new CombinedOldImage[fh.getParentCount()];
70  		for (int i = 0; i < old.length; i++) {
71  			final int imagePos = i;
72  			old[i] = new CombinedOldImage() {
73  				@Override
74  				public AbbreviatedObjectId getId() {
75  					return fh.getOldId(imagePos);
76  				}
77  			};
78  		}
79  	}
80  
81  	/** {@inheritDoc} */
82  	@Override
83  	public CombinedFileHeader getFileHeader() {
84  		return (CombinedFileHeader) super.getFileHeader();
85  	}
86  
87  	/** {@inheritDoc} */
88  	@Override
89  	public OldImage getOldImage() {
90  		return getOldImage(0);
91  	}
92  
93  	/**
94  	 * Get the OldImage data related to the nth ancestor
95  	 *
96  	 * @param nthParent
97  	 *            the ancestor to get the old image data of
98  	 * @return image data of the requested ancestor.
99  	 */
100 	public OldImage getOldImage(int nthParent) {
101 		return old[nthParent];
102 	}
103 
104 	@Override
105 	void parseHeader() {
106 		// Parse "@@@ -55,12 -163,13 +163,15 @@@ protected boolean"
107 		//
108 		final byte[] buf = file.buf;
109 		final MutableIntegerger.html#MutableInteger">MutableInteger ptr = new MutableInteger();
110 		ptr.value = nextLF(buf, startOffset, ' ');
111 
112 		for (int n = 0; n < old.length; n++) {
113 			old[n].startLine = -parseBase10(buf, ptr.value, ptr);
114 			if (buf[ptr.value] == ',')
115 				old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr);
116 			else
117 				old[n].lineCount = 1;
118 		}
119 
120 		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
121 		if (buf[ptr.value] == ',')
122 			newLineCount = parseBase10(buf, ptr.value + 1, ptr);
123 		else
124 			newLineCount = 1;
125 	}
126 
127 	@Override
128 	int parseBody(Patch script, int end) {
129 		final byte[] buf = file.buf;
130 		int c = nextLF(buf, startOffset);
131 
132 		for (CombinedOldImage o : old) {
133 			o.nDeleted = 0;
134 			o.nAdded = 0;
135 			o.nContext = 0;
136 		}
137 		nContext = 0;
138 		int nAdded = 0;
139 
140 		SCAN: for (int eol; c < end; c = eol) {
141 			eol = nextLF(buf, c);
142 
143 			if (eol - c < old.length + 1) {
144 				// Line isn't long enough to mention the state of each
145 				// ancestor. It must be the end of the hunk.
146 				break SCAN;
147 			}
148 
149 			switch (buf[c]) {
150 			case ' ':
151 			case '-':
152 			case '+':
153 				break;
154 
155 			default:
156 				// Line can't possibly be part of this hunk; the first
157 				// ancestor information isn't recognizable.
158 				//
159 				break SCAN;
160 			}
161 
162 			int localcontext = 0;
163 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
164 				switch (buf[c + ancestor]) {
165 				case ' ':
166 					localcontext++;
167 					old[ancestor].nContext++;
168 					continue;
169 
170 				case '-':
171 					old[ancestor].nDeleted++;
172 					continue;
173 
174 				case '+':
175 					old[ancestor].nAdded++;
176 					nAdded++;
177 					continue;
178 
179 				default:
180 					break SCAN;
181 				}
182 			}
183 			if (localcontext == old.length)
184 				nContext++;
185 		}
186 
187 		for (int ancestor = 0; ancestor < old.length; ancestor++) {
188 			final CombinedOldImage o = old[ancestor];
189 			final int cmp = o.nContext + o.nDeleted;
190 			if (cmp < o.lineCount) {
191 				final int missingCnt = o.lineCount - cmp;
192 				script.error(buf, startOffset, MessageFormat.format(
193 						JGitText.get().truncatedHunkLinesMissingForAncestor,
194 						Integer.valueOf(missingCnt),
195 						Integer.valueOf(ancestor + 1)));
196 			}
197 		}
198 
199 		if (nContext + nAdded < newLineCount) {
200 			final int missingCount = newLineCount - (nContext + nAdded);
201 			script.error(buf, startOffset, MessageFormat.format(
202 					JGitText.get().truncatedHunkNewLinesMissing,
203 					Integer.valueOf(missingCount)));
204 		}
205 
206 		return c;
207 	}
208 
209 	@Override
210 	void extractFileLines(OutputStream[] out) throws IOException {
211 		final byte[] buf = file.buf;
212 		int ptr = startOffset;
213 		int eol = nextLF(buf, ptr);
214 		if (endOffset <= eol)
215 			return;
216 
217 		// Treat the hunk header as though it were from the ancestor,
218 		// as it may have a function header appearing after it which
219 		// was copied out of the ancestor file.
220 		//
221 		out[0].write(buf, ptr, eol - ptr);
222 
223 		SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
224 			eol = nextLF(buf, ptr);
225 
226 			if (eol - ptr < old.length + 1) {
227 				// Line isn't long enough to mention the state of each
228 				// ancestor. It must be the end of the hunk.
229 				break SCAN;
230 			}
231 
232 			switch (buf[ptr]) {
233 			case ' ':
234 			case '-':
235 			case '+':
236 				break;
237 
238 			default:
239 				// Line can't possibly be part of this hunk; the first
240 				// ancestor information isn't recognizable.
241 				//
242 				break SCAN;
243 			}
244 
245 			int delcnt = 0;
246 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
247 				switch (buf[ptr + ancestor]) {
248 				case '-':
249 					delcnt++;
250 					out[ancestor].write(buf, ptr, eol - ptr);
251 					continue;
252 
253 				case ' ':
254 					out[ancestor].write(buf, ptr, eol - ptr);
255 					continue;
256 
257 				case '+':
258 					continue;
259 
260 				default:
261 					break SCAN;
262 				}
263 			}
264 			if (delcnt < old.length) {
265 				// This line appears in the new file if it wasn't deleted
266 				// relative to all ancestors.
267 				//
268 				out[old.length].write(buf, ptr, eol - ptr);
269 			}
270 		}
271 	}
272 
273 	@Override
274 	void extractFileLines(final StringBuilder sb, final String[] text,
275 			final int[] offsets) {
276 		final byte[] buf = file.buf;
277 		int ptr = startOffset;
278 		int eol = nextLF(buf, ptr);
279 		if (endOffset <= eol)
280 			return;
281 		copyLine(sb, text, offsets, 0);
282 		SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
283 			eol = nextLF(buf, ptr);
284 
285 			if (eol - ptr < old.length + 1) {
286 				// Line isn't long enough to mention the state of each
287 				// ancestor. It must be the end of the hunk.
288 				break SCAN;
289 			}
290 
291 			switch (buf[ptr]) {
292 			case ' ':
293 			case '-':
294 			case '+':
295 				break;
296 
297 			default:
298 				// Line can't possibly be part of this hunk; the first
299 				// ancestor information isn't recognizable.
300 				//
301 				break SCAN;
302 			}
303 
304 			boolean copied = false;
305 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
306 				switch (buf[ptr + ancestor]) {
307 				case ' ':
308 				case '-':
309 					if (copied)
310 						skipLine(text, offsets, ancestor);
311 					else {
312 						copyLine(sb, text, offsets, ancestor);
313 						copied = true;
314 					}
315 					continue;
316 
317 				case '+':
318 					continue;
319 
320 				default:
321 					break SCAN;
322 				}
323 			}
324 			if (!copied) {
325 				// If none of the ancestors caused the copy then this line
326 				// must be new across the board, so it only appears in the
327 				// text of the new file.
328 				//
329 				copyLine(sb, text, offsets, old.length);
330 			}
331 		}
332 	}
333 }