/* * Copyright (C) 2020, Thomas Wolf 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. *

* 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: *

    *
  1. call {@link #build()} without a signature set to obtain payload
  2. *
  3. create {@link GpgSignature} from payload
  4. *
  5. set {@link GpgSignature}
  6. *
*

* * @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 gpgsig * header. *

* 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 * not written; the caller is supposed to terminate the GPG * signature header by writing a single newline. *

* * @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'); } } }