CombinedHunkHeader.java

  1. /*
  2.  * Copyright (C) 2008, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.patch;

  11. import static org.eclipse.jgit.util.RawParseUtils.nextLF;
  12. import static org.eclipse.jgit.util.RawParseUtils.parseBase10;

  13. import java.io.IOException;
  14. import java.io.OutputStream;
  15. import java.text.MessageFormat;

  16. import org.eclipse.jgit.internal.JGitText;
  17. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  18. import org.eclipse.jgit.util.MutableInteger;

  19. /**
  20.  * Hunk header for a hunk appearing in a "diff --cc" style patch.
  21.  */
  22. public class CombinedHunkHeader extends HunkHeader {
  23.     private abstract static class CombinedOldImage extends OldImage {
  24.         int nContext;
  25.     }

  26.     private CombinedOldImage[] old;

  27.     CombinedHunkHeader(CombinedFileHeader fh, int offset) {
  28.         super(fh, offset, null);
  29.         old = new CombinedOldImage[fh.getParentCount()];
  30.         for (int i = 0; i < old.length; i++) {
  31.             final int imagePos = i;
  32.             old[i] = new CombinedOldImage() {
  33.                 @Override
  34.                 public AbbreviatedObjectId getId() {
  35.                     return fh.getOldId(imagePos);
  36.                 }
  37.             };
  38.         }
  39.     }

  40.     /** {@inheritDoc} */
  41.     @Override
  42.     public CombinedFileHeader getFileHeader() {
  43.         return (CombinedFileHeader) super.getFileHeader();
  44.     }

  45.     /** {@inheritDoc} */
  46.     @Override
  47.     public OldImage getOldImage() {
  48.         return getOldImage(0);
  49.     }

  50.     /**
  51.      * Get the OldImage data related to the nth ancestor
  52.      *
  53.      * @param nthParent
  54.      *            the ancestor to get the old image data of
  55.      * @return image data of the requested ancestor.
  56.      */
  57.     public OldImage getOldImage(int nthParent) {
  58.         return old[nthParent];
  59.     }

  60.     @Override
  61.     void parseHeader() {
  62.         // Parse "@@@ -55,12 -163,13 +163,15 @@@ protected boolean"
  63.         //
  64.         final byte[] buf = file.buf;
  65.         final MutableInteger ptr = new MutableInteger();
  66.         ptr.value = nextLF(buf, startOffset, ' ');

  67.         for (CombinedOldImage o : old) {
  68.             o.startLine = -parseBase10(buf, ptr.value, ptr);
  69.             if (buf[ptr.value] == ',') {
  70.                 o.lineCount = parseBase10(buf, ptr.value + 1, ptr);
  71.             } else {
  72.                 o.lineCount = 1;
  73.             }
  74.         }

  75.         newStartLine = parseBase10(buf, ptr.value + 1, ptr);
  76.         if (buf[ptr.value] == ',')
  77.             newLineCount = parseBase10(buf, ptr.value + 1, ptr);
  78.         else
  79.             newLineCount = 1;
  80.     }

  81.     @Override
  82.     int parseBody(Patch script, int end) {
  83.         final byte[] buf = file.buf;
  84.         int c = nextLF(buf, startOffset);

  85.         for (CombinedOldImage o : old) {
  86.             o.nDeleted = 0;
  87.             o.nAdded = 0;
  88.             o.nContext = 0;
  89.         }
  90.         nContext = 0;
  91.         int nAdded = 0;

  92.         SCAN: for (int eol; c < end; c = eol) {
  93.             eol = nextLF(buf, c);

  94.             if (eol - c < old.length + 1) {
  95.                 // Line isn't long enough to mention the state of each
  96.                 // ancestor. It must be the end of the hunk.
  97.                 break SCAN;
  98.             }

  99.             switch (buf[c]) {
  100.             case ' ':
  101.             case '-':
  102.             case '+':
  103.                 break;

  104.             default:
  105.                 // Line can't possibly be part of this hunk; the first
  106.                 // ancestor information isn't recognizable.
  107.                 //
  108.                 break SCAN;
  109.             }

  110.             int localcontext = 0;
  111.             for (int ancestor = 0; ancestor < old.length; ancestor++) {
  112.                 switch (buf[c + ancestor]) {
  113.                 case ' ':
  114.                     localcontext++;
  115.                     old[ancestor].nContext++;
  116.                     continue;

  117.                 case '-':
  118.                     old[ancestor].nDeleted++;
  119.                     continue;

  120.                 case '+':
  121.                     old[ancestor].nAdded++;
  122.                     nAdded++;
  123.                     continue;

  124.                 default:
  125.                     break SCAN;
  126.                 }
  127.             }
  128.             if (localcontext == old.length)
  129.                 nContext++;
  130.         }

  131.         for (int ancestor = 0; ancestor < old.length; ancestor++) {
  132.             final CombinedOldImage o = old[ancestor];
  133.             final int cmp = o.nContext + o.nDeleted;
  134.             if (cmp < o.lineCount) {
  135.                 final int missingCnt = o.lineCount - cmp;
  136.                 script.error(buf, startOffset, MessageFormat.format(
  137.                         JGitText.get().truncatedHunkLinesMissingForAncestor,
  138.                         Integer.valueOf(missingCnt),
  139.                         Integer.valueOf(ancestor + 1)));
  140.             }
  141.         }

  142.         if (nContext + nAdded < newLineCount) {
  143.             final int missingCount = newLineCount - (nContext + nAdded);
  144.             script.error(buf, startOffset, MessageFormat.format(
  145.                     JGitText.get().truncatedHunkNewLinesMissing,
  146.                     Integer.valueOf(missingCount)));
  147.         }

  148.         return c;
  149.     }

  150.     @Override
  151.     void extractFileLines(OutputStream[] out) throws IOException {
  152.         final byte[] buf = file.buf;
  153.         int ptr = startOffset;
  154.         int eol = nextLF(buf, ptr);
  155.         if (endOffset <= eol)
  156.             return;

  157.         // Treat the hunk header as though it were from the ancestor,
  158.         // as it may have a function header appearing after it which
  159.         // was copied out of the ancestor file.
  160.         //
  161.         out[0].write(buf, ptr, eol - ptr);

  162.         SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
  163.             eol = nextLF(buf, ptr);

  164.             if (eol - ptr < old.length + 1) {
  165.                 // Line isn't long enough to mention the state of each
  166.                 // ancestor. It must be the end of the hunk.
  167.                 break SCAN;
  168.             }

  169.             switch (buf[ptr]) {
  170.             case ' ':
  171.             case '-':
  172.             case '+':
  173.                 break;

  174.             default:
  175.                 // Line can't possibly be part of this hunk; the first
  176.                 // ancestor information isn't recognizable.
  177.                 //
  178.                 break SCAN;
  179.             }

  180.             int delcnt = 0;
  181.             for (int ancestor = 0; ancestor < old.length; ancestor++) {
  182.                 switch (buf[ptr + ancestor]) {
  183.                 case '-':
  184.                     delcnt++;
  185.                     out[ancestor].write(buf, ptr, eol - ptr);
  186.                     continue;

  187.                 case ' ':
  188.                     out[ancestor].write(buf, ptr, eol - ptr);
  189.                     continue;

  190.                 case '+':
  191.                     continue;

  192.                 default:
  193.                     break SCAN;
  194.                 }
  195.             }
  196.             if (delcnt < old.length) {
  197.                 // This line appears in the new file if it wasn't deleted
  198.                 // relative to all ancestors.
  199.                 //
  200.                 out[old.length].write(buf, ptr, eol - ptr);
  201.             }
  202.         }
  203.     }

  204.     @Override
  205.     void extractFileLines(final StringBuilder sb, final String[] text,
  206.             final int[] offsets) {
  207.         final byte[] buf = file.buf;
  208.         int ptr = startOffset;
  209.         int eol = nextLF(buf, ptr);
  210.         if (endOffset <= eol)
  211.             return;
  212.         copyLine(sb, text, offsets, 0);
  213.         SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
  214.             eol = nextLF(buf, ptr);

  215.             if (eol - ptr < old.length + 1) {
  216.                 // Line isn't long enough to mention the state of each
  217.                 // ancestor. It must be the end of the hunk.
  218.                 break SCAN;
  219.             }

  220.             switch (buf[ptr]) {
  221.             case ' ':
  222.             case '-':
  223.             case '+':
  224.                 break;

  225.             default:
  226.                 // Line can't possibly be part of this hunk; the first
  227.                 // ancestor information isn't recognizable.
  228.                 //
  229.                 break SCAN;
  230.             }

  231.             boolean copied = false;
  232.             for (int ancestor = 0; ancestor < old.length; ancestor++) {
  233.                 switch (buf[ptr + ancestor]) {
  234.                 case ' ':
  235.                 case '-':
  236.                     if (copied)
  237.                         skipLine(text, offsets, ancestor);
  238.                     else {
  239.                         copyLine(sb, text, offsets, ancestor);
  240.                         copied = true;
  241.                     }
  242.                     continue;

  243.                 case '+':
  244.                     continue;

  245.                 default:
  246.                     break SCAN;
  247.                 }
  248.             }
  249.             if (!copied) {
  250.                 // If none of the ancestors caused the copy then this line
  251.                 // must be new across the board, so it only appears in the
  252.                 // text of the new file.
  253.                 //
  254.                 copyLine(sb, text, offsets, old.length);
  255.             }
  256.         }
  257.     }
  258. }