diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java | 441 |
1 files changed, 319 insertions, 122 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java index 8f7e3eff7c..50f4a83b93 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java @@ -1,57 +1,33 @@ /* * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php + * 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. * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.lib; +import static java.time.ZoneOffset.UTC; + import java.io.Serializable; -import java.text.SimpleDateFormat; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.util.SystemReader; +import org.eclipse.jgit.util.time.ProposedTimestamp; /** * A combination of a person identity and time in Git. @@ -62,136 +38,333 @@ import org.eclipse.jgit.util.SystemReader; public class PersonIdent implements Serializable { private static final long serialVersionUID = 1L; + /** + * Get timezone object for the given offset. + * + * @param tzOffset + * timezone offset as in {@link #getTimeZoneOffset()}. + * @return time zone object for the given offset. + * @since 4.1 + * @deprecated use {@link #getZoneId(int)} instead + */ + @Deprecated(since = "7.2") + public static TimeZone getTimeZone(int tzOffset) { + StringBuilder tzId = new StringBuilder(8); + tzId.append("GMT"); //$NON-NLS-1$ + appendTimezone(tzId, tzOffset); + return TimeZone.getTimeZone(tzId.toString()); + } + + /** + * Translate a minutes offset into a ZoneId + * + * @param tzOffset as minutes east of UTC + * @return a ZoneId for this offset (UTC if invalid) + * @since 7.1 + */ + public static ZoneId getZoneId(int tzOffset) { + try { + return ZoneOffset.ofHoursMinutes(tzOffset / 60, tzOffset % 60); + } catch (DateTimeException e) { + return UTC; + } + } + + /** + * Format a timezone offset. + * + * @param r + * string builder to append to. + * @param offset + * timezone offset as in {@link #getTimeZoneOffset()}. + * @since 4.1 + */ + public static void appendTimezone(StringBuilder r, int offset) { + final char sign; + final int offsetHours; + final int offsetMins; + + if (offset < 0) { + sign = '-'; + offset = -offset; + } else { + sign = '+'; + } + + offsetHours = offset / 60; + offsetMins = offset % 60; + + r.append(sign); + if (offsetHours < 10) { + r.append('0'); + } + r.append(offsetHours); + if (offsetMins < 10) { + r.append('0'); + } + r.append(offsetMins); + } + + /** + * Sanitize the given string for use in an identity and append to output. + * <p> + * Trims whitespace from both ends and special characters {@code \n < >} that + * interfere with parsing; appends all other characters to the output. + * Analogous to the C git function {@code strbuf_addstr_without_crud}. + * + * @param r + * string builder to append to. + * @param str + * input string. + * @since 4.4 + */ + public static void appendSanitized(StringBuilder r, String str) { + // Trim any whitespace less than \u0020 as in String#trim(). + int i = 0; + while (i < str.length() && str.charAt(i) <= ' ') { + i++; + } + int end = str.length(); + while (end > i && str.charAt(end - 1) <= ' ') { + end--; + } + + for (; i < end; i++) { + char c = str.charAt(i); + switch (c) { + case '\n': + case '<': + case '>': + continue; + default: + r.append(c); + break; + } + } + } + + // Write offsets as [+-]HHMM + private static final DateTimeFormatter OFFSET_FORMATTER = DateTimeFormatter + .ofPattern("Z", Locale.US); //$NON-NLS-1$ + private final String name; private final String emailAddress; - private final long when; + private final Instant when; - private final int tzOffset; + private final ZoneId tzOffset; /** * Creates new PersonIdent from config info in repository, with current time. * This new PersonIdent gets the info from the default committer as available * from the configuration. * - * @param repo + * @param repo a {@link org.eclipse.jgit.lib.Repository} object. */ - public PersonIdent(final Repository repo) { + public PersonIdent(Repository repo) { this(repo.getConfig().get(UserConfig.KEY)); } /** - * Copy a {@link PersonIdent}. + * Copy a {@link org.eclipse.jgit.lib.PersonIdent}. * * @param pi - * Original {@link PersonIdent} + * Original {@link org.eclipse.jgit.lib.PersonIdent} */ - public PersonIdent(final PersonIdent pi) { + public PersonIdent(PersonIdent pi) { this(pi.getName(), pi.getEmailAddress()); } /** - * Construct a new {@link PersonIdent} with current time. + * Construct a new {@link org.eclipse.jgit.lib.PersonIdent} with current + * time. * * @param aName + * a {@link java.lang.String} object. * @param aEmailAddress + * a {@link java.lang.String} object. */ - public PersonIdent(final String aName, final String aEmailAddress) { - this(aName, aEmailAddress, SystemReader.getInstance().getCurrentTime()); + public PersonIdent(String aName, String aEmailAddress) { + this(aName, aEmailAddress, SystemReader.getInstance().now()); + } + + /** + * Construct a new {@link org.eclipse.jgit.lib.PersonIdent} with current + * time. + * + * @param aName + * a {@link java.lang.String} object. + * @param aEmailAddress + * a {@link java.lang.String} object. + * @param when + * a {@link org.eclipse.jgit.util.time.ProposedTimestamp} object. + * @since 4.6 + */ + public PersonIdent(String aName, String aEmailAddress, + ProposedTimestamp when) { + this(aName, aEmailAddress, when.instant()); } /** * Copy a PersonIdent, but alter the clone's time stamp * * @param pi - * original {@link PersonIdent} + * original {@link org.eclipse.jgit.lib.PersonIdent} * @param when * local time * @param tz * time zone + * @deprecated Use {@link #PersonIdent(PersonIdent, Instant, ZoneId)} instead. + */ + @Deprecated(since = "7.1") + public PersonIdent(PersonIdent pi, Date when, TimeZone tz) { + this(pi.getName(), pi.getEmailAddress(), when.toInstant(), tz.toZoneId()); + } + + /** + * Copy a PersonIdent, but alter the clone's time stamp + * + * @param pi + * original {@link org.eclipse.jgit.lib.PersonIdent} + * @param when + * local time + * @param tz + * time zone offset + * @since 7.1 */ - public PersonIdent(final PersonIdent pi, final Date when, final TimeZone tz) { + public PersonIdent(PersonIdent pi, Instant when, ZoneId tz) { this(pi.getName(), pi.getEmailAddress(), when, tz); } /** - * Copy a {@link PersonIdent}, but alter the clone's time stamp + * Copy a {@link org.eclipse.jgit.lib.PersonIdent}, but alter the clone's + * time stamp * * @param pi - * original {@link PersonIdent} + * original {@link org.eclipse.jgit.lib.PersonIdent} * @param aWhen * local time + * @deprecated Use the variant with an Instant instead + */ + @Deprecated(since = "7.1") + public PersonIdent(PersonIdent pi, Date aWhen) { + this(pi.getName(), pi.getEmailAddress(), aWhen.toInstant(), + pi.tzOffset); + } + + /** + * Copy a {@link org.eclipse.jgit.lib.PersonIdent}, but alter the clone's + * time stamp + * + * @param pi + * original {@link org.eclipse.jgit.lib.PersonIdent} + * @param aWhen + * local time as Instant + * @since 6.1 */ - public PersonIdent(final PersonIdent pi, final Date aWhen) { - this(pi.getName(), pi.getEmailAddress(), aWhen.getTime(), pi.tzOffset); + public PersonIdent(PersonIdent pi, Instant aWhen) { + this(pi.getName(), pi.getEmailAddress(), aWhen, pi.tzOffset); } /** * Construct a PersonIdent from simple data * - * @param aName - * @param aEmailAddress + * @param aName a {@link java.lang.String} object. + * @param aEmailAddress a {@link java.lang.String} object. * @param aWhen * local time stamp * @param aTZ * time zone + * @deprecated Use the variant with Instant and ZoneId instead */ + @Deprecated(since = "7.1") public PersonIdent(final String aName, final String aEmailAddress, final Date aWhen, final TimeZone aTZ) { - this(aName, aEmailAddress, aWhen.getTime(), aTZ.getOffset(aWhen - .getTime()) / (60 * 1000)); + this(aName, aEmailAddress, aWhen.toInstant(), aTZ.toZoneId()); + } + + /** + * Construct a PersonIdent from simple data + * + * @param aName + * a {@link java.lang.String} object. + * @param aEmailAddress + * a {@link java.lang.String} object. + * @param aWhen + * local time stamp + * @param zoneId + * time zone id + * @since 6.1 + */ + public PersonIdent(final String aName, String aEmailAddress, Instant aWhen, + ZoneId zoneId) { + if (aName == null) + throw new IllegalArgumentException( + JGitText.get().personIdentNameNonNull); + if (aEmailAddress == null) + throw new IllegalArgumentException( + JGitText.get().personIdentEmailNonNull); + name = aName; + emailAddress = aEmailAddress; + when = aWhen; + tzOffset = zoneId; } /** * Copy a PersonIdent, but alter the clone's time stamp * * @param pi - * original {@link PersonIdent} + * original {@link org.eclipse.jgit.lib.PersonIdent} * @param aWhen * local time stamp * @param aTZ * time zone + * @deprecated Use the variant with Instant and ZoneId instead */ - public PersonIdent(final PersonIdent pi, final long aWhen, final int aTZ) { - this(pi.getName(), pi.getEmailAddress(), aWhen, aTZ); + @Deprecated(since = "7.1") + public PersonIdent(PersonIdent pi, long aWhen, int aTZ) { + this(pi.getName(), pi.getEmailAddress(), Instant.ofEpochMilli(aWhen), + getZoneId(aTZ)); } private PersonIdent(final String aName, final String aEmailAddress, - long when) { + Instant when) { this(aName, aEmailAddress, when, SystemReader.getInstance() - .getTimezone(when)); + .getTimeZoneAt(when)); } - private PersonIdent(final UserConfig config) { + private PersonIdent(UserConfig config) { this(config.getCommitterName(), config.getCommitterEmail()); } /** - * Construct a {@link PersonIdent} + * Construct a {@link org.eclipse.jgit.lib.PersonIdent}. + * <p> + * Whitespace in the name and email is preserved for the lifetime of this + * object, but are trimmed by {@link #toExternalString()}. This means that + * parsing the result of {@link #toExternalString()} may not return an + * equivalent instance. * * @param aName + * a {@link java.lang.String} object. * @param aEmailAddress + * a {@link java.lang.String} object. * @param aWhen * local time stamp * @param aTZ * time zone + * @deprecated Use the variant with Instant and ZoneId instead */ + @Deprecated(since = "7.1") public PersonIdent(final String aName, final String aEmailAddress, final long aWhen, final int aTZ) { - if (aName == null) - throw new IllegalArgumentException( - "Name of PersonIdent must not be null."); - if (aEmailAddress == null) - throw new IllegalArgumentException( - "E-mail address of PersonIdent must not be null."); - name = aName; - emailAddress = aEmailAddress; - when = aWhen; - tzOffset = aTZ; + this(aName, aEmailAddress, Instant.ofEpochMilli(aWhen), getZoneId(aTZ)); } /** + * Get name of person + * * @return Name of person */ public String getName() { @@ -199,6 +372,8 @@ public class PersonIdent implements Serializable { } /** + * Get email address of person + * * @return email address of person */ public String getEmailAddress() { @@ -206,43 +381,93 @@ public class PersonIdent implements Serializable { } /** + * Get timestamp + * * @return timestamp + * + * @deprecated Use getWhenAsInstant instead */ + @Deprecated(since = "7.1") public Date getWhen() { - return new Date(when); + return Date.from(when); } /** + * Get when attribute as instant + * + * @return timestamp + * @since 6.1 + */ + public Instant getWhenAsInstant() { + return when; + } + + /** + * Get this person's declared time zone + * * @return this person's declared time zone; null if time zone is unknown. + * + * @deprecated Use getZoneId instead */ + @Deprecated(since = "7.1") public TimeZone getTimeZone() { - StringBuilder tzId = new StringBuilder(8); - tzId.append("GMT"); //$NON-NLS-1$ - appendTimezone(tzId); - return TimeZone.getTimeZone(tzId.toString()); + return TimeZone.getTimeZone(tzOffset); } /** + * Get the time zone id + * + * @return the time zone id + * @since 6.1 + */ + public ZoneId getZoneId() { + return tzOffset; + } + + /** + * Return the offset in this timezone at the specific time + * + * @return the offset + * @since 7.1 + */ + public ZoneOffset getZoneOffset() { + return tzOffset.getRules().getOffset(when); + } + + /** + * Get this person's declared time zone as minutes east of UTC. + * * @return this person's declared time zone as minutes east of UTC. If the * timezone is to the west of UTC it is negative. + * @deprecated Use {@link #getZoneOffset()} and read minutes from there */ + @Deprecated(since = "7.1") public int getTimeZoneOffset() { - return tzOffset; + return getZoneOffset().getTotalSeconds() / 60; } + /** + * {@inheritDoc} + * <p> + * Hashcode is based only on the email address and timestamp. + */ + @Override public int hashCode() { int hc = getEmailAddress().hashCode(); hc *= 31; - hc += (int) (when / 1000L); + hc += when.hashCode(); return hc; } - public boolean equals(final Object o) { + @Override + public boolean equals(Object o) { if (o instanceof PersonIdent) { final PersonIdent p = (PersonIdent) o; return getName().equals(p.getName()) && getEmailAddress().equals(p.getEmailAddress()) - && when / 1000L == p.when / 1000L; + // commmit timestamps are stored with 1 second precision + && when.truncatedTo(ChronoUnit.SECONDS) + .equals(p.when.truncatedTo(ChronoUnit.SECONDS)); } return false; } @@ -254,58 +479,30 @@ public class PersonIdent implements Serializable { */ public String toExternalString() { final StringBuilder r = new StringBuilder(); - r.append(getName().trim()); + appendSanitized(r, getName()); r.append(" <"); //$NON-NLS-1$ - r.append(getEmailAddress().trim()); + appendSanitized(r, getEmailAddress()); r.append("> "); //$NON-NLS-1$ - r.append(when / 1000); + r.append(when.toEpochMilli() / 1000); r.append(' '); - appendTimezone(r); + r.append(OFFSET_FORMATTER.format(getZoneOffset())); return r.toString(); } - private void appendTimezone(final StringBuilder r) { - int offset = tzOffset; - final char sign; - final int offsetHours; - final int offsetMins; - - if (offset < 0) { - sign = '-'; - offset = -offset; - } else { - sign = '+'; - } - - offsetHours = offset / 60; - offsetMins = offset % 60; - - r.append(sign); - if (offsetHours < 10) { - r.append('0'); - } - r.append(offsetHours); - if (offsetMins < 10) { - r.append('0'); - } - r.append(offsetMins); - } - + @Override @SuppressWarnings("nls") public String toString() { final StringBuilder r = new StringBuilder(); - final SimpleDateFormat dtfmt; - dtfmt = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy Z", Locale.US); - dtfmt.setTimeZone(getTimeZone()); - + DateTimeFormatter dtfmt = DateTimeFormatter + .ofPattern("EEE MMM d HH:mm:ss yyyy Z", Locale.US) //$NON-NLS-1$ + .withZone(tzOffset); r.append("PersonIdent["); r.append(getName()); r.append(", "); r.append(getEmailAddress()); r.append(", "); - r.append(dtfmt.format(Long.valueOf(when))); + r.append(dtfmt.format(when)); r.append("]"); - return r.toString(); } } |