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