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 (CombinedOldImage o : old) {
113 			o.startLine = -parseBase10(buf, ptr.value, ptr);
114 			if (buf[ptr.value] == ',') {
115 				o.lineCount = parseBase10(buf, ptr.value + 1, ptr);
116 			} else {
117 				o.lineCount = 1;
118 			}
119 		}
120 
121 		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
122 		if (buf[ptr.value] == ',')
123 			newLineCount = parseBase10(buf, ptr.value + 1, ptr);
124 		else
125 			newLineCount = 1;
126 	}
127 
128 	@Override
129 	int parseBody(Patch script, int end) {
130 		final byte[] buf = file.buf;
131 		int c = nextLF(buf, startOffset);
132 
133 		for (CombinedOldImage o : old) {
134 			o.nDeleted = 0;
135 			o.nAdded = 0;
136 			o.nContext = 0;
137 		}
138 		nContext = 0;
139 		int nAdded = 0;
140 
141 		SCAN: for (int eol; c < end; c = eol) {
142 			eol = nextLF(buf, c);
143 
144 			if (eol - c < old.length + 1) {
145 				// Line isn't long enough to mention the state of each
146 				// ancestor. It must be the end of the hunk.
147 				break SCAN;
148 			}
149 
150 			switch (buf[c]) {
151 			case ' ':
152 			case '-':
153 			case '+':
154 				break;
155 
156 			default:
157 				// Line can't possibly be part of this hunk; the first
158 				// ancestor information isn't recognizable.
159 				//
160 				break SCAN;
161 			}
162 
163 			int localcontext = 0;
164 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
165 				switch (buf[c + ancestor]) {
166 				case ' ':
167 					localcontext++;
168 					old[ancestor].nContext++;
169 					continue;
170 
171 				case '-':
172 					old[ancestor].nDeleted++;
173 					continue;
174 
175 				case '+':
176 					old[ancestor].nAdded++;
177 					nAdded++;
178 					continue;
179 
180 				default:
181 					break SCAN;
182 				}
183 			}
184 			if (localcontext == old.length)
185 				nContext++;
186 		}
187 
188 		for (int ancestor = 0; ancestor < old.length; ancestor++) {
189 			final CombinedOldImage o = old[ancestor];
190 			final int cmp = o.nContext + o.nDeleted;
191 			if (cmp < o.lineCount) {
192 				final int missingCnt = o.lineCount - cmp;
193 				script.error(buf, startOffset, MessageFormat.format(
194 						JGitText.get().truncatedHunkLinesMissingForAncestor,
195 						Integer.valueOf(missingCnt),
196 						Integer.valueOf(ancestor + 1)));
197 			}
198 		}
199 
200 		if (nContext + nAdded < newLineCount) {
201 			final int missingCount = newLineCount - (nContext + nAdded);
202 			script.error(buf, startOffset, MessageFormat.format(
203 					JGitText.get().truncatedHunkNewLinesMissing,
204 					Integer.valueOf(missingCount)));
205 		}
206 
207 		return c;
208 	}
209 
210 	@Override
211 	void extractFileLines(OutputStream[] out) throws IOException {
212 		final byte[] buf = file.buf;
213 		int ptr = startOffset;
214 		int eol = nextLF(buf, ptr);
215 		if (endOffset <= eol)
216 			return;
217 
218 		// Treat the hunk header as though it were from the ancestor,
219 		// as it may have a function header appearing after it which
220 		// was copied out of the ancestor file.
221 		//
222 		out[0].write(buf, ptr, eol - ptr);
223 
224 		SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
225 			eol = nextLF(buf, ptr);
226 
227 			if (eol - ptr < old.length + 1) {
228 				// Line isn't long enough to mention the state of each
229 				// ancestor. It must be the end of the hunk.
230 				break SCAN;
231 			}
232 
233 			switch (buf[ptr]) {
234 			case ' ':
235 			case '-':
236 			case '+':
237 				break;
238 
239 			default:
240 				// Line can't possibly be part of this hunk; the first
241 				// ancestor information isn't recognizable.
242 				//
243 				break SCAN;
244 			}
245 
246 			int delcnt = 0;
247 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
248 				switch (buf[ptr + ancestor]) {
249 				case '-':
250 					delcnt++;
251 					out[ancestor].write(buf, ptr, eol - ptr);
252 					continue;
253 
254 				case ' ':
255 					out[ancestor].write(buf, ptr, eol - ptr);
256 					continue;
257 
258 				case '+':
259 					continue;
260 
261 				default:
262 					break SCAN;
263 				}
264 			}
265 			if (delcnt < old.length) {
266 				// This line appears in the new file if it wasn't deleted
267 				// relative to all ancestors.
268 				//
269 				out[old.length].write(buf, ptr, eol - ptr);
270 			}
271 		}
272 	}
273 
274 	@Override
275 	void extractFileLines(final StringBuilder sb, final String[] text,
276 			final int[] offsets) {
277 		final byte[] buf = file.buf;
278 		int ptr = startOffset;
279 		int eol = nextLF(buf, ptr);
280 		if (endOffset <= eol)
281 			return;
282 		copyLine(sb, text, offsets, 0);
283 		SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
284 			eol = nextLF(buf, ptr);
285 
286 			if (eol - ptr < old.length + 1) {
287 				// Line isn't long enough to mention the state of each
288 				// ancestor. It must be the end of the hunk.
289 				break SCAN;
290 			}
291 
292 			switch (buf[ptr]) {
293 			case ' ':
294 			case '-':
295 			case '+':
296 				break;
297 
298 			default:
299 				// Line can't possibly be part of this hunk; the first
300 				// ancestor information isn't recognizable.
301 				//
302 				break SCAN;
303 			}
304 
305 			boolean copied = false;
306 			for (int ancestor = 0; ancestor < old.length; ancestor++) {
307 				switch (buf[ptr + ancestor]) {
308 				case ' ':
309 				case '-':
310 					if (copied)
311 						skipLine(text, offsets, ancestor);
312 					else {
313 						copyLine(sb, text, offsets, ancestor);
314 						copied = true;
315 					}
316 					continue;
317 
318 				case '+':
319 					continue;
320 
321 				default:
322 					break SCAN;
323 				}
324 			}
325 			if (!copied) {
326 				// If none of the ancestors caused the copy then this line
327 				// must be new across the board, so it only appears in the
328 				// text of the new file.
329 				//
330 				copyLine(sb, text, offsets, old.length);
331 			}
332 		}
333 	}
334 }