ObjectBuilder.java
/*
* Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.lib;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Objects;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.References;
/**
* Common base class for {@link CommitBuilder} and {@link TagBuilder}.
*
* @since 5.11
*/
public abstract class ObjectBuilder {
/** Byte representation of "encoding". */
private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
private PersonIdent author;
private GpgSignature gpgSignature;
private String message;
private Charset encoding = StandardCharsets.UTF_8;
/**
* Retrieves the author of this object.
*
* @return the author of this object, or {@code null} if not set yet
*/
protected PersonIdent getAuthor() {
return author;
}
/**
* Sets the author (name, email address, and date) of this object.
*
* @param newAuthor
* the new author, must be non-{@code null}
*/
protected void setAuthor(PersonIdent newAuthor) {
author = Objects.requireNonNull(newAuthor);
}
/**
* Sets the GPG signature of this object.
* <p>
* Note, the signature set here will change the payload of the object, i.e.
* the output of {@link #build()} will include the signature. Thus, the
* typical flow will be:
* <ol>
* <li>call {@link #build()} without a signature set to obtain payload</li>
* <li>create {@link GpgSignature} from payload</li>
* <li>set {@link GpgSignature}</li>
* </ol>
* </p>
*
* @param gpgSignature
* the signature to set or {@code null} to unset
* @since 5.3
*/
public void setGpgSignature(@Nullable GpgSignature gpgSignature) {
this.gpgSignature = gpgSignature;
}
/**
* Retrieves the GPG signature of this object.
*
* @return the GPG signature of this object, or {@code null} if the object
* is not signed
* @since 5.3
*/
@Nullable
public GpgSignature getGpgSignature() {
return gpgSignature;
}
/**
* Retrieves the complete message of the object.
*
* @return the complete message; can be {@code null}.
*/
@Nullable
public String getMessage() {
return message;
}
/**
* Sets the message (commit message, or message of an annotated tag).
*
* @param message
* the message.
*/
public void setMessage(@Nullable String message) {
this.message = message;
}
/**
* Retrieves the encoding that should be used for the message text.
*
* @return the encoding that should be used for the message text.
*/
@NonNull
public Charset getEncoding() {
return encoding;
}
/**
* Sets the encoding for the object message.
*
* @param encoding
* the encoding to use.
*/
public void setEncoding(@NonNull Charset encoding) {
this.encoding = encoding;
}
/**
* Format this builder's state as a git object.
*
* @return this object in the canonical git format, suitable for storage in
* a repository.
* @throws java.io.UnsupportedEncodingException
* the encoding specified by {@link #getEncoding()} is not
* supported by this Java runtime.
*/
@NonNull
public abstract byte[] build() throws UnsupportedEncodingException;
/**
* Writes signature to output as per <a href=
* "https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt#L66,L89">gpgsig
* header</a>.
* <p>
* CRLF and CR will be sanitized to LF and signature will have a hanging
* indent of one space starting with line two. A trailing line break is
* <em>not</em> written; the caller is supposed to terminate the GPG
* signature header by writing a single newline.
* </p>
*
* @param in
* signature string with line breaks
* @param out
* output stream
* @param enforceAscii
* whether to throw {@link IllegalArgumentException} if non-ASCII
* characters are encountered
* @throws IOException
* thrown by the output stream
* @throws IllegalArgumentException
* if the signature string contains non 7-bit ASCII chars and
* {@code enforceAscii == true}
*/
static void writeMultiLineHeader(@NonNull String in,
@NonNull OutputStream out, boolean enforceAscii)
throws IOException, IllegalArgumentException {
int length = in.length();
for (int i = 0; i < length; ++i) {
char ch = in.charAt(i);
switch (ch) {
case '\r':
if (i + 1 < length && in.charAt(i + 1) == '\n') {
++i;
}
if (i + 1 < length) {
out.write('\n');
out.write(' ');
}
break;
case '\n':
if (i + 1 < length) {
out.write('\n');
out.write(' ');
}
break;
default:
// sanity check
if (ch > 127 && enforceAscii)
throw new IllegalArgumentException(MessageFormat
.format(JGitText.get().notASCIIString, in));
out.write(ch);
break;
}
}
}
/**
* Writes an "encoding" header.
*
* @param encoding
* to write
* @param out
* to write to
* @throws IOException
* if writing fails
*/
static void writeEncoding(@NonNull Charset encoding,
@NonNull OutputStream out) throws IOException {
if (!References.isSameObject(encoding, UTF_8)) {
out.write(hencoding);
out.write(' ');
out.write(Constants.encodeASCII(encoding.name()));
out.write('\n');
}
}
}