123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- /*
- * Copyright (C) 2008-2009, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- package org.eclipse.jgit.patch;
-
- import static org.eclipse.jgit.util.RawParseUtils.match;
- import static org.eclipse.jgit.util.RawParseUtils.nextLF;
- import static org.eclipse.jgit.util.RawParseUtils.parseBase10;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.text.MessageFormat;
-
- import org.eclipse.jgit.JGitText;
- import org.eclipse.jgit.diff.Edit;
- import org.eclipse.jgit.diff.EditList;
- import org.eclipse.jgit.lib.AbbreviatedObjectId;
- import org.eclipse.jgit.util.MutableInteger;
-
- /** Hunk header describing the layout of a single block of lines */
- public class HunkHeader {
- /** Details about an old image of the file. */
- public abstract static class OldImage {
- /** First line number the hunk starts on in this file. */
- int startLine;
-
- /** Total number of lines this hunk covers in this file. */
- int lineCount;
-
- /** Number of lines deleted by the post-image from this file. */
- int nDeleted;
-
- /** Number of lines added by the post-image not in this file. */
- int nAdded;
-
- /** @return first line number the hunk starts on in this file. */
- public int getStartLine() {
- return startLine;
- }
-
- /** @return total number of lines this hunk covers in this file. */
- public int getLineCount() {
- return lineCount;
- }
-
- /** @return number of lines deleted by the post-image from this file. */
- public int getLinesDeleted() {
- return nDeleted;
- }
-
- /** @return number of lines added by the post-image not in this file. */
- public int getLinesAdded() {
- return nAdded;
- }
-
- /** @return object id of the pre-image file. */
- public abstract AbbreviatedObjectId getId();
- }
-
- final FileHeader file;
-
- /** Offset within {@link #file}.buf to the "@@ -" line. */
- final int startOffset;
-
- /** Position 1 past the end of this hunk within {@link #file}'s buf. */
- int endOffset;
-
- private final OldImage old;
-
- /** First line number in the post-image file where the hunk starts */
- int newStartLine;
-
- /** Total number of post-image lines this hunk covers (context + inserted) */
- int newLineCount;
-
- /** Total number of lines of context appearing in this hunk */
- int nContext;
-
- private EditList editList;
-
- HunkHeader(final FileHeader fh, final int offset) {
- this(fh, offset, new OldImage() {
- @Override
- public AbbreviatedObjectId getId() {
- return fh.getOldId();
- }
- });
- }
-
- HunkHeader(final FileHeader fh, final int offset, final OldImage oi) {
- file = fh;
- startOffset = offset;
- old = oi;
- }
-
- HunkHeader(final FileHeader fh, final EditList editList) {
- this(fh, fh.buf.length);
- this.editList = editList;
- endOffset = startOffset;
- nContext = 0;
- if (editList.isEmpty()) {
- newStartLine = 0;
- newLineCount = 0;
- } else {
- newStartLine = editList.get(0).getBeginB();
- Edit last = editList.get(editList.size() - 1);
- newLineCount = last.getEndB() - newStartLine;
- }
- }
-
- /** @return header for the file this hunk applies to */
- public FileHeader getFileHeader() {
- return file;
- }
-
- /** @return the byte array holding this hunk's patch script. */
- public byte[] getBuffer() {
- return file.buf;
- }
-
- /** @return offset the start of this hunk in {@link #getBuffer()}. */
- public int getStartOffset() {
- return startOffset;
- }
-
- /** @return offset one past the end of the hunk in {@link #getBuffer()}. */
- public int getEndOffset() {
- return endOffset;
- }
-
- /** @return information about the old image mentioned in this hunk. */
- public OldImage getOldImage() {
- return old;
- }
-
- /** @return first line number in the post-image file where the hunk starts */
- public int getNewStartLine() {
- return newStartLine;
- }
-
- /** @return Total number of post-image lines this hunk covers */
- public int getNewLineCount() {
- return newLineCount;
- }
-
- /** @return total number of lines of context appearing in this hunk */
- public int getLinesContext() {
- return nContext;
- }
-
- /** @return a list describing the content edits performed within the hunk. */
- public EditList toEditList() {
- if (editList == null) {
- editList = new EditList();
- final byte[] buf = file.buf;
- int c = nextLF(buf, startOffset);
- int oLine = old.startLine;
- int nLine = newStartLine;
- Edit in = null;
-
- SCAN: for (; c < endOffset; c = nextLF(buf, c)) {
- switch (buf[c]) {
- case ' ':
- case '\n':
- in = null;
- oLine++;
- nLine++;
- continue;
-
- case '-':
- if (in == null) {
- in = new Edit(oLine - 1, nLine - 1);
- editList.add(in);
- }
- oLine++;
- in.extendA();
- continue;
-
- case '+':
- if (in == null) {
- in = new Edit(oLine - 1, nLine - 1);
- editList.add(in);
- }
- nLine++;
- in.extendB();
- continue;
-
- case '\\': // Matches "\ No newline at end of file"
- continue;
-
- default:
- break SCAN;
- }
- }
- }
- return editList;
- }
-
- void parseHeader() {
- // Parse "@@ -236,9 +236,9 @@ protected boolean"
- //
- final byte[] buf = file.buf;
- final MutableInteger ptr = new MutableInteger();
- ptr.value = nextLF(buf, startOffset, ' ');
- old.startLine = -parseBase10(buf, ptr.value, ptr);
- if (buf[ptr.value] == ',')
- old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
- else
- old.lineCount = 1;
-
- newStartLine = parseBase10(buf, ptr.value + 1, ptr);
- if (buf[ptr.value] == ',')
- newLineCount = parseBase10(buf, ptr.value + 1, ptr);
- else
- newLineCount = 1;
- }
-
- int parseBody(final Patch script, final int end) {
- final byte[] buf = file.buf;
- int c = nextLF(buf, startOffset), last = c;
-
- old.nDeleted = 0;
- old.nAdded = 0;
-
- SCAN: for (; c < end; last = c, c = nextLF(buf, c)) {
- switch (buf[c]) {
- case ' ':
- case '\n':
- nContext++;
- continue;
-
- case '-':
- old.nDeleted++;
- continue;
-
- case '+':
- old.nAdded++;
- continue;
-
- case '\\': // Matches "\ No newline at end of file"
- continue;
-
- default:
- break SCAN;
- }
- }
-
- if (last < end && nContext + old.nDeleted - 1 == old.lineCount
- && nContext + old.nAdded == newLineCount
- && match(buf, last, Patch.SIG_FOOTER) >= 0) {
- // This is an extremely common occurrence of "corruption".
- // Users add footers with their signatures after this mark,
- // and git diff adds the git executable version number.
- // Let it slide; the hunk otherwise looked sound.
- //
- old.nDeleted--;
- return last;
- }
-
- if (nContext + old.nDeleted < old.lineCount) {
- final int missingCount = old.lineCount - (nContext + old.nDeleted);
- script.error(buf, startOffset, MessageFormat.format(
- JGitText.get().truncatedHunkOldLinesMissing, missingCount));
-
- } else if (nContext + old.nAdded < newLineCount) {
- final int missingCount = newLineCount - (nContext + old.nAdded);
- script.error(buf, startOffset, MessageFormat.format(
- JGitText.get().truncatedHunkNewLinesMissing, missingCount));
-
- } else if (nContext + old.nDeleted > old.lineCount
- || nContext + old.nAdded > newLineCount) {
- final String oldcnt = old.lineCount + ":" + newLineCount;
- final String newcnt = (nContext + old.nDeleted) + ":"
- + (nContext + old.nAdded);
- script.warn(buf, startOffset, MessageFormat.format(
- JGitText.get().hunkHeaderDoesNotMatchBodyLineCountOf, oldcnt, newcnt));
- }
-
- return c;
- }
-
- void extractFileLines(final OutputStream[] out) throws IOException {
- final byte[] buf = file.buf;
- int ptr = startOffset;
- int eol = nextLF(buf, ptr);
- if (endOffset <= eol)
- return;
-
- // Treat the hunk header as though it were from the ancestor,
- // as it may have a function header appearing after it which
- // was copied out of the ancestor file.
- //
- out[0].write(buf, ptr, eol - ptr);
-
- SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
- eol = nextLF(buf, ptr);
- switch (buf[ptr]) {
- case ' ':
- case '\n':
- case '\\':
- out[0].write(buf, ptr, eol - ptr);
- out[1].write(buf, ptr, eol - ptr);
- break;
- case '-':
- out[0].write(buf, ptr, eol - ptr);
- break;
- case '+':
- out[1].write(buf, ptr, eol - ptr);
- break;
- default:
- break SCAN;
- }
- }
- }
-
- void extractFileLines(final StringBuilder sb, final String[] text,
- final int[] offsets) {
- final byte[] buf = file.buf;
- int ptr = startOffset;
- int eol = nextLF(buf, ptr);
- if (endOffset <= eol)
- return;
- copyLine(sb, text, offsets, 0);
- SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
- eol = nextLF(buf, ptr);
- switch (buf[ptr]) {
- case ' ':
- case '\n':
- case '\\':
- copyLine(sb, text, offsets, 0);
- skipLine(text, offsets, 1);
- break;
- case '-':
- copyLine(sb, text, offsets, 0);
- break;
- case '+':
- copyLine(sb, text, offsets, 1);
- break;
- default:
- break SCAN;
- }
- }
- }
-
- void copyLine(final StringBuilder sb, final String[] text,
- final int[] offsets, final int fileIdx) {
- final String s = text[fileIdx];
- final int start = offsets[fileIdx];
- int end = s.indexOf('\n', start);
- if (end < 0)
- end = s.length();
- else
- end++;
- sb.append(s, start, end);
- offsets[fileIdx] = end;
- }
-
- void skipLine(final String[] text, final int[] offsets,
- final int fileIdx) {
- final String s = text[fileIdx];
- final int end = s.indexOf('\n', offsets[fileIdx]);
- offsets[fileIdx] = end < 0 ? s.length() : end + 1;
- }
- }
|