001// SPDX-FileCopyrightText: 2020 Paul Schaub <vanitasvitae@fsfe.org>, 2021 Flowcrypt a.s.
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.key.util;
006
007import javax.annotation.Nonnull;
008
009public final class UserId implements CharSequence {
010    public static final class Builder {
011        private String name;
012        private String comment;
013        private String email;
014
015        private Builder() {
016        }
017
018        private Builder(String name, String comment, String email) {
019            this.name = name;
020            this.comment = comment;
021            this.email = email;
022        }
023
024        public Builder withName(String name) {
025            checkNotNull("name", name);
026            this.name = name;
027            return this;
028        }
029
030        public Builder withComment(String comment) {
031            checkNotNull("comment", comment);
032            this.comment = comment;
033            return this;
034        }
035
036        public Builder withEmail(String email) {
037            checkNotNull("email", email);
038            this.email = email;
039            return this;
040        }
041
042        public Builder noName() {
043            name = null;
044            return this;
045        }
046
047        public Builder noComment() {
048            comment = null;
049            return this;
050        }
051
052        public Builder noEmail() {
053            email = null;
054            return this;
055        }
056
057        public UserId build() {
058            return new UserId(name, comment, email);
059        }
060    }
061
062    private final String name;
063    private final String comment;
064    private final String email;
065    private long hash = Long.MAX_VALUE;
066
067    private UserId(String name, String comment, String email) {
068        this.name = name;
069        this.comment = comment;
070        this.email = email;
071    }
072
073    public static UserId onlyEmail(String email) {
074        checkNotNull("email", email);
075        return new UserId(null, null, email);
076    }
077
078    public static UserId nameAndEmail(String name, String email) {
079        checkNotNull("name", name);
080        checkNotNull("email", email);
081        return new UserId(name, null, email);
082    }
083
084    public static Builder newBuilder() {
085        return new Builder();
086    }
087
088    public Builder toBuilder() {
089        return new Builder(name, comment, email);
090    }
091
092    public String getName() {
093        return name;
094    }
095
096    public String getComment() {
097        return comment;
098    }
099
100    public String getEmail() {
101        return email;
102    }
103
104    @Override
105    public int length() {
106        return toString().length();
107    }
108
109    @Override
110    public char charAt(int i) {
111        return toString().charAt(i);
112    }
113
114    @Override
115    public @Nonnull CharSequence subSequence(int i, int i1) {
116        return toString().subSequence(i, i1);
117    }
118
119    @Override
120    public @Nonnull String toString() {
121        return asString(false);
122    }
123
124    /**
125     * Returns a string representation of the object.
126     * @param ignoreEmptyValues Flag which indicates that empty string values should not be outputted.
127     * @return a string representation of the object.
128     */
129    public String asString(boolean ignoreEmptyValues) {
130        StringBuilder sb = new StringBuilder();
131        if (name != null && (!ignoreEmptyValues || !name.isEmpty())) {
132            sb.append(name);
133        }
134        if (comment != null && (!ignoreEmptyValues || !comment.isEmpty())) {
135            sb.append(" (").append(comment).append(')');
136        }
137        if (email != null && (!ignoreEmptyValues || !email.isEmpty())) {
138            final boolean moreThanJustEmail = sb.length() > 0;
139            if (moreThanJustEmail) sb.append(" <");
140            sb.append(email);
141            if (moreThanJustEmail) sb.append('>');
142        }
143        return sb.toString();
144    }
145
146    @Override
147    public boolean equals(Object o) {
148        if (o == null) return false;
149        if (o == this) return true;
150        if (!(o instanceof UserId)) return false;
151        final UserId other = (UserId) o;
152        return isEqualComponent(name, other.name, false)
153                && isEqualComponent(comment, other.comment, false)
154                && isEqualComponent(email, other.email, true);
155    }
156
157    @Override
158    public int hashCode() {
159        if (hash != Long.MAX_VALUE) {
160            return (int) hash;
161        } else {
162            int hashCode = 7;
163            hashCode = 31 * hashCode + (name == null ? 0 : name.hashCode());
164            hashCode = 31 * hashCode + (comment == null ? 0 : comment.hashCode());
165            hashCode = 31 * hashCode + (email == null ? 0 : email.toLowerCase().hashCode());
166            this.hash = hashCode;
167            return hashCode;
168        }
169    }
170
171    private static boolean isEqualComponent(String value, String otherValue, boolean ignoreCase) {
172        final boolean valueIsNull = (value == null);
173        final boolean otherValueIsNull = (otherValue == null);
174        return (valueIsNull && otherValueIsNull)
175                || (!valueIsNull && !otherValueIsNull
176                && (ignoreCase ? value.equalsIgnoreCase(otherValue) : value.equals(otherValue)));
177    }
178
179    private static void checkNotNull(String paramName, String value) {
180        if (value == null) {
181            throw new IllegalArgumentException(paramName + " must be not null");
182        }
183    }
184}