You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectBuilder.java 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> 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.lib;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import java.io.IOException;
  13. import java.io.OutputStream;
  14. import java.io.UnsupportedEncodingException;
  15. import java.nio.charset.Charset;
  16. import java.nio.charset.StandardCharsets;
  17. import java.text.MessageFormat;
  18. import java.util.Objects;
  19. import org.eclipse.jgit.annotations.NonNull;
  20. import org.eclipse.jgit.annotations.Nullable;
  21. import org.eclipse.jgit.internal.JGitText;
  22. import org.eclipse.jgit.util.References;
  23. /**
  24. * Common base class for {@link CommitBuilder} and {@link TagBuilder}.
  25. *
  26. * @since 5.11
  27. */
  28. public abstract class ObjectBuilder {
  29. /** Byte representation of "encoding". */
  30. private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
  31. private PersonIdent author;
  32. private GpgSignature gpgSignature;
  33. private String message;
  34. private Charset encoding = StandardCharsets.UTF_8;
  35. /**
  36. * Retrieves the author of this object.
  37. *
  38. * @return the author of this object, or {@code null} if not set yet
  39. */
  40. protected PersonIdent getAuthor() {
  41. return author;
  42. }
  43. /**
  44. * Sets the author (name, email address, and date) of this object.
  45. *
  46. * @param newAuthor
  47. * the new author, must be non-{@code null}
  48. */
  49. protected void setAuthor(PersonIdent newAuthor) {
  50. author = Objects.requireNonNull(newAuthor);
  51. }
  52. /**
  53. * Sets the GPG signature of this object.
  54. * <p>
  55. * Note, the signature set here will change the payload of the object, i.e.
  56. * the output of {@link #build()} will include the signature. Thus, the
  57. * typical flow will be:
  58. * <ol>
  59. * <li>call {@link #build()} without a signature set to obtain payload</li>
  60. * <li>create {@link GpgSignature} from payload</li>
  61. * <li>set {@link GpgSignature}</li>
  62. * </ol>
  63. * </p>
  64. *
  65. * @param gpgSignature
  66. * the signature to set or {@code null} to unset
  67. * @since 5.3
  68. */
  69. public void setGpgSignature(@Nullable GpgSignature gpgSignature) {
  70. this.gpgSignature = gpgSignature;
  71. }
  72. /**
  73. * Retrieves the GPG signature of this object.
  74. *
  75. * @return the GPG signature of this object, or {@code null} if the object
  76. * is not signed
  77. * @since 5.3
  78. */
  79. @Nullable
  80. public GpgSignature getGpgSignature() {
  81. return gpgSignature;
  82. }
  83. /**
  84. * Retrieves the complete message of the object.
  85. *
  86. * @return the complete message; can be {@code null}.
  87. */
  88. @Nullable
  89. public String getMessage() {
  90. return message;
  91. }
  92. /**
  93. * Sets the message (commit message, or message of an annotated tag).
  94. *
  95. * @param message
  96. * the message.
  97. */
  98. public void setMessage(@Nullable String message) {
  99. this.message = message;
  100. }
  101. /**
  102. * Retrieves the encoding that should be used for the message text.
  103. *
  104. * @return the encoding that should be used for the message text.
  105. */
  106. @NonNull
  107. public Charset getEncoding() {
  108. return encoding;
  109. }
  110. /**
  111. * Sets the encoding for the object message.
  112. *
  113. * @param encoding
  114. * the encoding to use.
  115. */
  116. public void setEncoding(@NonNull Charset encoding) {
  117. this.encoding = encoding;
  118. }
  119. /**
  120. * Format this builder's state as a git object.
  121. *
  122. * @return this object in the canonical git format, suitable for storage in
  123. * a repository.
  124. * @throws java.io.UnsupportedEncodingException
  125. * the encoding specified by {@link #getEncoding()} is not
  126. * supported by this Java runtime.
  127. */
  128. @NonNull
  129. public abstract byte[] build() throws UnsupportedEncodingException;
  130. /**
  131. * Writes signature to output as per <a href=
  132. * "https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt#L66,L89">gpgsig
  133. * header</a>.
  134. * <p>
  135. * CRLF and CR will be sanitized to LF and signature will have a hanging
  136. * indent of one space starting with line two. A trailing line break is
  137. * <em>not</em> written; the caller is supposed to terminate the GPG
  138. * signature header by writing a single newline.
  139. * </p>
  140. *
  141. * @param in
  142. * signature string with line breaks
  143. * @param out
  144. * output stream
  145. * @param enforceAscii
  146. * whether to throw {@link IllegalArgumentException} if non-ASCII
  147. * characters are encountered
  148. * @throws IOException
  149. * thrown by the output stream
  150. * @throws IllegalArgumentException
  151. * if the signature string contains non 7-bit ASCII chars and
  152. * {@code enforceAscii == true}
  153. */
  154. static void writeMultiLineHeader(@NonNull String in,
  155. @NonNull OutputStream out, boolean enforceAscii)
  156. throws IOException, IllegalArgumentException {
  157. int length = in.length();
  158. for (int i = 0; i < length; ++i) {
  159. char ch = in.charAt(i);
  160. switch (ch) {
  161. case '\r':
  162. if (i + 1 < length && in.charAt(i + 1) == '\n') {
  163. ++i;
  164. }
  165. if (i + 1 < length) {
  166. out.write('\n');
  167. out.write(' ');
  168. }
  169. break;
  170. case '\n':
  171. if (i + 1 < length) {
  172. out.write('\n');
  173. out.write(' ');
  174. }
  175. break;
  176. default:
  177. // sanity check
  178. if (ch > 127 && enforceAscii)
  179. throw new IllegalArgumentException(MessageFormat
  180. .format(JGitText.get().notASCIIString, in));
  181. out.write(ch);
  182. break;
  183. }
  184. }
  185. }
  186. /**
  187. * Writes an "encoding" header.
  188. *
  189. * @param encoding
  190. * to write
  191. * @param out
  192. * to write to
  193. * @throws IOException
  194. * if writing fails
  195. */
  196. static void writeEncoding(@NonNull Charset encoding,
  197. @NonNull OutputStream out) throws IOException {
  198. if (!References.isSameObject(encoding, UTF_8)) {
  199. out.write(hencoding);
  200. out.write(' ');
  201. out.write(Constants.encodeASCII(encoding.name()));
  202. out.write('\n');
  203. }
  204. }
  205. }