MergeFormatterPass.java

  1. /*
  2.  * Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
  3.  * Copyright (C) 2014, AndrĂ© de Oliveira <andre.oliveira@liferay.com> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.merge;

  12. import java.io.IOException;
  13. import java.io.OutputStream;
  14. import java.nio.charset.Charset;
  15. import java.util.List;

  16. import org.eclipse.jgit.diff.RawText;
  17. import org.eclipse.jgit.merge.MergeChunk.ConflictState;

  18. class MergeFormatterPass {

  19.     private final EolAwareOutputStream out;

  20.     private final MergeResult<RawText> res;

  21.     private final List<String> seqName;

  22.     private final Charset charset;

  23.     private final boolean threeWayMerge;

  24.     private String lastConflictingName; // is set to non-null whenever we are in
  25.                                         // a conflict

  26.     /**
  27.      * @param out
  28.      *            the {@link java.io.OutputStream} where to write the textual
  29.      *            presentation
  30.      * @param res
  31.      *            the merge result which should be presented
  32.      * @param seqName
  33.      *            When a conflict is reported each conflicting range will get a
  34.      *            name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
  35.      *            " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict markers. The
  36.      *            names for the sequences are given in this list
  37.      * @param charset
  38.      *            the character set used when writing conflict metadata
  39.      */
  40.     MergeFormatterPass(OutputStream out, MergeResult<RawText> res,
  41.             List<String> seqName, Charset charset) {
  42.         this.out = new EolAwareOutputStream(out);
  43.         this.res = res;
  44.         this.seqName = seqName;
  45.         this.charset = charset;
  46.         this.threeWayMerge = (res.getSequences().size() == 3);
  47.     }

  48.     void formatMerge() throws IOException {
  49.         boolean missingNewlineAtEnd = false;
  50.         for (MergeChunk chunk : res) {
  51.             RawText seq = res.getSequences().get(chunk.getSequenceIndex());
  52.             writeConflictMetadata(chunk);
  53.             // the lines with conflict-metadata are written. Now write the chunk
  54.             for (int i = chunk.getBegin(); i < chunk.getEnd(); i++)
  55.                 writeLine(seq, i);
  56.             missingNewlineAtEnd = seq.isMissingNewlineAtEnd();
  57.         }
  58.         // one possible leftover: if the merge result ended with a conflict we
  59.         // have to close the last conflict here
  60.         if (lastConflictingName != null)
  61.             writeConflictEnd();
  62.         if (!missingNewlineAtEnd)
  63.             out.beginln();
  64.     }

  65.     private void writeConflictMetadata(MergeChunk chunk) throws IOException {
  66.         if (lastConflictingName != null
  67.                 && chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
  68.             // found the end of an conflict
  69.             writeConflictEnd();
  70.         }
  71.         if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
  72.             // found the start of an conflict
  73.             writeConflictStart(chunk);
  74.         } else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
  75.             // found another conflicting chunk
  76.             writeConflictChange(chunk);
  77.         }
  78.     }

  79.     private void writeConflictEnd() throws IOException {
  80.         writeln(">>>>>>> " + lastConflictingName); //$NON-NLS-1$
  81.         lastConflictingName = null;
  82.     }

  83.     private void writeConflictStart(MergeChunk chunk) throws IOException {
  84.         lastConflictingName = seqName.get(chunk.getSequenceIndex());
  85.         writeln("<<<<<<< " + lastConflictingName); //$NON-NLS-1$
  86.     }

  87.     private void writeConflictChange(MergeChunk chunk) throws IOException {
  88.         /*
  89.          * In case of a non-three-way merge I'll add the name of the conflicting
  90.          * chunk behind the equal signs. I also append the name of the last
  91.          * conflicting chunk after the ending greater-than signs. If somebody
  92.          * knows a better notation to present non-three-way merges - feel free
  93.          * to correct here.
  94.          */
  95.         lastConflictingName = seqName.get(chunk.getSequenceIndex());
  96.         writeln(threeWayMerge ? "=======" : "======= " //$NON-NLS-1$ //$NON-NLS-2$
  97.                 + lastConflictingName);
  98.     }

  99.     private void writeln(String s) throws IOException {
  100.         out.beginln();
  101.         out.write((s + "\n").getBytes(charset)); //$NON-NLS-1$
  102.     }

  103.     private void writeLine(RawText seq, int i) throws IOException {
  104.         out.beginln();
  105.         seq.writeLine(out, i);
  106.         // still BOL? It was a blank line. But writeLine won't lf, so we do.
  107.         if (out.isBeginln())
  108.             out.write('\n');
  109.     }
  110. }