001package ezvcard.property;
002
003import java.util.ArrayList;
004import java.util.LinkedHashMap;
005import java.util.List;
006import java.util.Map;
007
008import ezvcard.VCard;
009import ezvcard.VCardVersion;
010import ezvcard.ValidationWarning;
011
012/*
013 Copyright (c) 2012-2023, Michael Angstadt
014 All rights reserved.
015
016 Redistribution and use in source and binary forms, with or without
017 modification, are permitted provided that the following conditions are met: 
018
019 1. Redistributions of source code must retain the above copyright notice, this
020 list of conditions and the following disclaimer. 
021 2. Redistributions in binary form must reproduce the above copyright notice,
022 this list of conditions and the following disclaimer in the documentation
023 and/or other materials provided with the distribution. 
024
025 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
026 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
027 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
028 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
029 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
030 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
031 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
032 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
033 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
034 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
035
036 The views and conclusions contained in the software and documentation are those
037 of the authors and should not be interpreted as representing official policies, 
038 either expressed or implied, of the FreeBSD Project.
039 */
040
041/**
042 * <p>
043 * Defines the individual components of the person's name.
044 * </p>
045 * 
046 * <p>
047 * <b>Code sample</b>
048 * </p>
049 * 
050 * <pre class="brush:java">
051 * VCard vcard = new VCard();
052 * 
053 * StructuredName n = new StructuredName();
054 * n.setFamily("House");
055 * n.setGiven("Gregory");
056 * n.getPrefixes().add("Dr");
057 * n.getSuffixes().add("MD");
058 * vcard.setStructuredName(n);
059 * </pre>
060 * 
061 * <p>
062 * <b>Property name:</b> {@code N}
063 * </p>
064 * <p>
065 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0}
066 * </p>
067 * @author Michael Angstadt
068 * @see <a href="http://tools.ietf.org/html/rfc6350#page-29">RFC 6350 p.29</a>
069 * @see <a href="http://tools.ietf.org/html/rfc2426#page-9">RFC 2426 p.9</a>
070 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.9</a>
071 */
072public class StructuredName extends VCardProperty implements HasAltId {
073        private String family;
074        private String given;
075        private final List<String> additional;
076        private final List<String> prefixes;
077        private final List<String> suffixes;
078
079        public StructuredName() {
080                additional = new ArrayList<>();
081                prefixes = new ArrayList<>();
082                suffixes = new ArrayList<>();
083        }
084
085        /**
086         * Copy constructor.
087         * @param original the property to make a copy of
088         */
089        public StructuredName(StructuredName original) {
090                super(original);
091                family = original.family;
092                given = original.given;
093                additional = new ArrayList<>(original.additional);
094                prefixes = new ArrayList<>(original.prefixes);
095                suffixes = new ArrayList<>(original.suffixes);
096        }
097
098        /**
099         * Gets the family name (aka "surname" or "last name").
100         * @return the family name or null if not set
101         */
102        public String getFamily() {
103                return family;
104        }
105
106        /**
107         * Sets the family name (aka "surname" or "last name").
108         * @param family the family name or null to remove
109         */
110        public void setFamily(String family) {
111                this.family = family;
112        }
113
114        /**
115         * Gets the given name (aka "first name").
116         * @return the given name or null if not set
117         */
118        public String getGiven() {
119                return given;
120        }
121
122        /**
123         * Sets the given name (aka "first name").
124         * @param given the given name or null to remove
125         */
126        public void setGiven(String given) {
127                this.given = given;
128        }
129
130        /**
131         * Gets the list that stores additional names the person goes by (for
132         * example, a middle name).
133         * @return the additional names (this list is mutable)
134         */
135        public List<String> getAdditionalNames() {
136                return additional;
137        }
138
139        /**
140         * Gets the list that stores the person's honorific prefixes.
141         * @return the prefixes (e.g. "Dr.", "Mr.") (this list is mutable)
142         */
143        public List<String> getPrefixes() {
144                return prefixes;
145        }
146
147        /**
148         * Gets the list that stores the person's honorary suffixes.
149         * @return the suffixes (e.g. "M.D.", "Jr.") (this list is mutable)
150         */
151        public List<String> getSuffixes() {
152                return suffixes;
153        }
154
155        /**
156         * <p>
157         * Gets the list that holds string(s) which define how to sort the vCard.
158         * </p>
159         * <p>
160         * 2.1 and 3.0 vCards should use the {@link SortString} property instead.
161         * </p>
162         * <p>
163         * <b>Supported versions:</b> {@code 4.0}
164         * </p>
165         * @return the sort string(s) (this list is mutable). For example, if the
166         * family name is "d'Aboville" and the given name is "Christine", the sort
167         * strings might be ["Aboville", "Christine"].
168         */
169        public List<String> getSortAs() {
170                return parameters.getSortAs();
171        }
172
173        /**
174         * <p>
175         * Defines a sortable version of the person's name.
176         * </p>
177         * <p>
178         * 2.1 and 3.0 vCards should use the {@link SortString} property instead.
179         * </p>
180         * <p>
181         * <b>Supported versions:</b> {@code 4.0}
182         * </p>
183         * @param family the sortable version of the family name (for example,
184         * "Adboville" if the family name is "d'Aboville") or null to remove
185         */
186        public void setSortAs(String family) {
187                parameters.setSortAs(family);
188        }
189
190        /**
191         * <p>
192         * Defines a sortable version of the person's name.
193         * </p>
194         * <p>
195         * 2.1 and 3.0 vCards should use the {@link SortString} property instead.
196         * </p>
197         * <p>
198         * <b>Supported versions:</b> {@code 4.0}
199         * </p>
200         * @param family the sortable version of the family name (for example,
201         * "Adboville" if the family name is "d'Aboville")
202         * @param given the sortable version of the given name
203         */
204        public void setSortAs(String family, String given) {
205                parameters.setSortAs(family, given);
206        }
207
208        @Override
209        public String getLanguage() {
210                return super.getLanguage();
211        }
212
213        @Override
214        public void setLanguage(String language) {
215                super.setLanguage(language);
216        }
217
218        //@Override
219        public String getAltId() {
220                return parameters.getAltId();
221        }
222
223        //@Override
224        public void setAltId(String altId) {
225                parameters.setAltId(altId);
226        }
227
228        @Override
229        protected Map<String, Object> toStringValues() {
230                Map<String, Object> values = new LinkedHashMap<>();
231                values.put("family", family);
232                values.put("given", given);
233                values.put("additional", additional);
234                values.put("prefixes", prefixes);
235                values.put("suffixes", suffixes);
236                return values;
237        }
238
239        @Override
240        protected void _validate(List<ValidationWarning> warnings, VCardVersion version, VCard vcard) {
241                /*
242                 * 2.1 does not allow multi-valued components.
243                 */
244                if (version == VCardVersion.V2_1) {
245                        //@formatter:off
246                        if (additional.size() > 1 ||
247                                prefixes.size() > 1 ||
248                                suffixes.size() > 1) {
249                                warnings.add(new ValidationWarning(34));
250                        }
251                        //@formatter:on
252                }
253        }
254
255        @Override
256        public StructuredName copy() {
257                return new StructuredName(this);
258        }
259
260        @Override
261        public int hashCode() {
262                final int prime = 31;
263                int result = super.hashCode();
264                result = prime * result + additional.hashCode();
265                result = prime * result + ((family == null) ? 0 : family.hashCode());
266                result = prime * result + ((given == null) ? 0 : given.hashCode());
267                result = prime * result + prefixes.hashCode();
268                result = prime * result + suffixes.hashCode();
269                return result;
270        }
271
272        @Override
273        public boolean equals(Object obj) {
274                if (this == obj) return true;
275                if (!super.equals(obj)) return false;
276                StructuredName other = (StructuredName) obj;
277                if (!additional.equals(other.additional)) return false;
278                if (family == null) {
279                        if (other.family != null) return false;
280                } else if (!family.equals(other.family)) return false;
281                if (given == null) {
282                        if (other.given != null) return false;
283                } else if (!given.equals(other.given)) return false;
284                if (!prefixes.equals(other.prefixes)) return false;
285                if (!suffixes.equals(other.suffixes)) return false;
286                return true;
287        }
288}