blob: 1bdb807a2f77a464cda320bae4b4e51fb11016c5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.vault.util.diff;
import java.io.IOException;
/**
* A hunk3 represents a block of a change from the 3-way diff. it can either be
* a modification on the left side (for which {@link #getLeftRange()} is not
* {@code null}), or a change on the right side (for which
* {@link #getRightRange()} is not {@code null}. if the left and the right
* ranges are {@code null} this hunk represents an unmodified block of the
* base document. if both ranges are NOT {@code null} it represents a
* conflicting block.
*/
public class Hunk3 {
/**
* the left document marker pattern
*/
public static final String[] MARKER_L = new String[]{"<<<<<<< ", ".mine"};
/**
* the right document marker pattern
*/
public static final String[] MARKER_R = new String[]{">>>>>>> ", ".theirs"};
/**
* the base document marker pattern
*/
public static final String[] MARKER_B = new String[]{"||||||| ", ".base"};
/**
* the separation marker pattern
*/
public static final String[] MARKER_M = new String[]{"=======", ""};
/**
* the base range. never {@code null}.
*/
private final Range base;
/**
* the left range
*/
private final Range left;
/**
* the right range
*/
private final Range right;
/**
* the next hunk in this chain
*/
private Hunk3 next;
/**
* Constructs a new hunk and appends it to the previous one.
* @param base the base document
* @param left the left document
* @param right the right document
* @param prev the previous hunk
*/
public Hunk3(Range base, Range left, Range right, Hunk3 prev) {
this.base = base;
this.left = left;
this.right = right;
if (prev != null) {
prev.next = this;
}
}
/**
* Returns the next hunk in this chain or {@code null} of this hunk is
* the last one.
* @return the next hunk.
*/
public Hunk3 next() {
return next;
}
/**
* Returns the range of the base document this hunk spans.
* @return the base range.
*/
public Range getBaseRange() {
return base;
}
/**
* Returns the range of the left document this hunk spans.
* can be {@code null}.
* @return the left range.
*/
public Range getLeftRange() {
return left;
}
/**
* Returns the range of the right document this hunk spans.
* can be {@code null}.
* @return the right range.
*/
public Range getRightRange() {
return right;
}
/**
* Writes this hunk to a writer. if this hunk represents a conflict it is
* included in a merge-like manner using the separators defined above.
*
* @param out the writer
* @param showBase include the block of the base document in case of a
* conflict as well.
* @throws IOException if a I/O error occurs.
*/
public void write(DiffWriter out, boolean showBase) throws IOException {
boolean conflict = left != null && right != null;
if (conflict) {
out.write(getMarker(MARKER_L, left.doc));
out.writeNewLine();
} else {
showBase = left == null && right == null;
}
if (left != null) {
int len = Math.min(left.high, left.doc.getElements().length);
for (int i = left.low; i < len; i++) {
out.write(left.doc.getElements()[i].getString());
}
}
if (showBase) {
if (conflict) {
out.write(getMarker(MARKER_B, base.doc));
out.writeNewLine();
}
int len = Math.min(base.high, base.doc.getElements().length);
for (int i = base.low; i < len; i++) {
out.write(base.doc.getElements()[i].getString());
}
}
if (conflict) {
out.write(getMarker(MARKER_M, null));
out.writeNewLine();
}
if (right != null) {
int len = Math.min(right.high, right.doc.getElements().length);
for (int i = right.low; i < len; i++) {
out.write(right.doc.getElements()[i].getString());
}
}
if (conflict) {
out.write(getMarker(MARKER_R, right.doc));
out.writeNewLine();
}
}
/**
* generate a debug string
* @return a debug string
*/
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("@@ =").append(base).append(" -").append(left).append(" +").append(right).append(" @@\n");
if (left != null) {
for (int i = 0; i < left.len(); i++) {
addLineNumbers(buf, i);
buf.append("< ");
buf.append(left.doc.getElements()[i + left.low]);
}
}
if (base != null) {
for (int i = 0; i < base.len(); i++) {
addLineNumbers(buf, i);
buf.append("= ");
buf.append(base.doc.getElements()[i + base.low]);
}
}
if (right != null) {
for (int i = 0; i < right.len(); i++) {
addLineNumbers(buf, i);
buf.append("> ");
buf.append(right.doc.getElements()[i + right.low]);
}
}
return buf.toString();
}
/**
* adds some line numbers to the string buffer
* @param buf the buffer
* @param i the index
*/
private void addLineNumbers(StringBuffer buf, int i) {
buf.append("(").append(i + base.low);
if (left != null) {
buf.append(",").append(i + left.low);
}
if (right != null) {
buf.append(",").append(i + right.low);
}
buf.append(") ");
}
/**
* Returns the marker string for the given format and document
* @param fmt the marker format
* @param doc the document or {@code null}.
* @return the marker string
*/
public static String getMarker(String[] fmt, Document doc) {
StringBuffer buf = new StringBuffer(fmt[0]);
if (doc != null && doc.getSource() != null && doc.getSource().getLabel() != null) {
buf.append(doc.getSource().getLabel());
} else {
buf.append(fmt[1]);
}
return buf.toString();
}
}