Fix wrong project name in file headers.
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Profile.java
1 /*
2  * Sone - Profile.java - Copyright © 2010 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.data;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.UUID;
24
25 import net.pterodactylus.util.validation.Validation;
26
27 /**
28  * A profile stores personal information about a {@link Sone}. All information
29  * is optional and can be {@code null}.
30  *
31  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
32  */
33 public class Profile implements Fingerprintable {
34
35         /** The first name. */
36         private volatile String firstName;
37
38         /** The middle name(s). */
39         private volatile String middleName;
40
41         /** The last name. */
42         private volatile String lastName;
43
44         /** The day of the birth date. */
45         private volatile Integer birthDay;
46
47         /** The month of the birth date. */
48         private volatile Integer birthMonth;
49
50         /** The year of the birth date. */
51         private volatile Integer birthYear;
52
53         /** Additional fields in the profile. */
54         private final List<Field> fields = Collections.synchronizedList(new ArrayList<Field>());
55
56         /**
57          * Creates a new empty profile.
58          */
59         public Profile() {
60                 /* do nothing. */
61         }
62
63         /**
64          * Creates a copy of a profile.
65          *
66          * @param profile
67          *            The profile to copy
68          */
69         public Profile(Profile profile) {
70                 if (profile == null) {
71                         return;
72                 }
73                 this.firstName = profile.firstName;
74                 this.middleName = profile.middleName;
75                 this.lastName = profile.lastName;
76                 this.birthDay = profile.birthDay;
77                 this.birthMonth = profile.birthMonth;
78                 this.birthYear = profile.birthYear;
79                 this.fields.addAll(profile.fields);
80         }
81
82         //
83         // ACCESSORS
84         //
85
86         /**
87          * Returns the first name.
88          *
89          * @return The first name
90          */
91         public String getFirstName() {
92                 return firstName;
93         }
94
95         /**
96          * Sets the first name.
97          *
98          * @param firstName
99          *            The first name to set
100          * @return This profile (for method chaining)
101          */
102         public Profile setFirstName(String firstName) {
103                 this.firstName = firstName;
104                 return this;
105         }
106
107         /**
108          * Returns the middle name(s).
109          *
110          * @return The middle name
111          */
112         public String getMiddleName() {
113                 return middleName;
114         }
115
116         /**
117          * Sets the middle name.
118          *
119          * @param middleName
120          *            The middle name to set
121          * @return This profile (for method chaining)
122          */
123         public Profile setMiddleName(String middleName) {
124                 this.middleName = middleName;
125                 return this;
126         }
127
128         /**
129          * Returns the last name.
130          *
131          * @return The last name
132          */
133         public String getLastName() {
134                 return lastName;
135         }
136
137         /**
138          * Sets the last name.
139          *
140          * @param lastName
141          *            The last name to set
142          * @return This profile (for method chaining)
143          */
144         public Profile setLastName(String lastName) {
145                 this.lastName = lastName;
146                 return this;
147         }
148
149         /**
150          * Returns the day of the birth date.
151          *
152          * @return The day of the birth date (from 1 to 31)
153          */
154         public Integer getBirthDay() {
155                 return birthDay;
156         }
157
158         /**
159          * Sets the day of the birth date.
160          *
161          * @param birthDay
162          *            The day of the birth date (from 1 to 31)
163          * @return This profile (for method chaining)
164          */
165         public Profile setBirthDay(Integer birthDay) {
166                 this.birthDay = birthDay;
167                 return this;
168         }
169
170         /**
171          * Returns the month of the birth date.
172          *
173          * @return The month of the birth date (from 1 to 12)
174          */
175         public Integer getBirthMonth() {
176                 return birthMonth;
177         }
178
179         /**
180          * Sets the month of the birth date.
181          *
182          * @param birthMonth
183          *            The month of the birth date (from 1 to 12)
184          * @return This profile (for method chaining)
185          */
186         public Profile setBirthMonth(Integer birthMonth) {
187                 this.birthMonth = birthMonth;
188                 return this;
189         }
190
191         /**
192          * Returns the year of the birth date.
193          *
194          * @return The year of the birth date
195          */
196         public Integer getBirthYear() {
197                 return birthYear;
198         }
199
200         /**
201          * Sets the year of the birth date.
202          *
203          * @param birthYear
204          *            The year of the birth date
205          * @return This profile (for method chaining)
206          */
207         public Profile setBirthYear(Integer birthYear) {
208                 this.birthYear = birthYear;
209                 return this;
210         }
211
212         /**
213          * Returns the fields of this profile.
214          *
215          * @return The fields of this profile
216          */
217         public List<Field> getFields() {
218                 return new ArrayList<Field>(fields);
219         }
220
221         /**
222          * Returns whether this profile contains the given field.
223          *
224          * @param field
225          *            The field to check for
226          * @return {@code true} if this profile contains the field, false otherwise
227          */
228         public boolean hasField(Field field) {
229                 return fields.contains(field);
230         }
231
232         /**
233          * Returns the field with the given ID.
234          *
235          * @param fieldId
236          *            The ID of the field to get
237          * @return The field, or {@code null} if this profile does not contain a
238          *         field with the given ID
239          */
240         public Field getFieldById(String fieldId) {
241                 Validation.begin().isNotNull("Field ID", fieldId).check();
242                 for (Field field : fields) {
243                         if (field.getId().equals(fieldId)) {
244                                 return field;
245                         }
246                 }
247                 return null;
248         }
249
250         /**
251          * Returns the field with the given name.
252          *
253          * @param fieldName
254          *            The name of the field to get
255          * @return The field, or {@code null} if this profile does not contain a
256          *         field with the given name
257          */
258         public Field getFieldByName(String fieldName) {
259                 for (Field field : fields) {
260                         if (field.getName().equals(fieldName)) {
261                                 return field;
262                         }
263                 }
264                 return null;
265         }
266
267         /**
268          * Appends a new field to the list of fields.
269          *
270          * @param fieldName
271          *            The name of the new field
272          * @return The new field
273          * @throws IllegalArgumentException
274          *             if the name is not valid
275          */
276         public Field addField(String fieldName) throws IllegalArgumentException {
277                 Validation.begin().isNotNull("Field Name", fieldName).check().isGreater("Field Name Length", fieldName.length(), 0).isNull("Field Name Unique", getFieldByName(fieldName)).check();
278                 @SuppressWarnings("synthetic-access")
279                 Field field = new Field().setName(fieldName);
280                 fields.add(field);
281                 return field;
282         }
283
284         /**
285          * Moves the given field up one position in the field list. The index of the
286          * field to move must be greater than {@code 0} (because you obviously can
287          * not move the first field further up).
288          *
289          * @param field
290          *            The field to move up
291          */
292         public void moveFieldUp(Field field) {
293                 Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isGreater("Field Index", getFieldIndex(field), 0).check();
294                 int fieldIndex = getFieldIndex(field);
295                 fields.remove(field);
296                 fields.add(fieldIndex - 1, field);
297         }
298
299         /**
300          * Moves the given field down one position in the field list. The index of
301          * the field to move must be less than the index of the last field (because
302          * you obviously can not move the last field further down).
303          *
304          * @param field
305          *            The field to move down
306          */
307         public void moveFieldDown(Field field) {
308                 Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isLess("Field Index", getFieldIndex(field), fields.size() - 1).check();
309                 int fieldIndex = getFieldIndex(field);
310                 fields.remove(field);
311                 fields.add(fieldIndex + 1, field);
312         }
313
314         /**
315          * Removes the given field.
316          *
317          * @param field
318          *            The field to remove
319          */
320         public void removeField(Field field) {
321                 Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).check();
322                 fields.remove(field);
323         }
324
325         //
326         // PRIVATE METHODS
327         //
328
329         /**
330          * Returns the index of the field with the given name.
331          *
332          * @param field
333          *            The name of the field
334          * @return The index of the field, or {@code -1} if there is no field with
335          *         the given name
336          */
337         private int getFieldIndex(Field field) {
338                 return fields.indexOf(field);
339         }
340
341         //
342         // INTERFACE Fingerprintable
343         //
344
345         /**
346          * {@inheritDoc}
347          */
348         @Override
349         public String getFingerprint() {
350                 StringBuilder fingerprint = new StringBuilder();
351                 fingerprint.append("Profile(");
352                 if (firstName != null) {
353                         fingerprint.append("FirstName(").append(firstName).append(')');
354                 }
355                 if (middleName != null) {
356                         fingerprint.append("MiddleName(").append(middleName).append(')');
357                 }
358                 if (lastName != null) {
359                         fingerprint.append("LastName(").append(lastName).append(')');
360                 }
361                 if (birthDay != null) {
362                         fingerprint.append("BirthDay(").append(birthDay).append(')');
363                 }
364                 if (birthMonth != null) {
365                         fingerprint.append("BirthMonth(").append(birthMonth).append(')');
366                 }
367                 if (birthYear != null) {
368                         fingerprint.append("BirthYear(").append(birthYear).append(')');
369                 }
370                 fingerprint.append("ContactInformation(");
371                 for (Field field : fields) {
372                         fingerprint.append(field.getName()).append('(').append(field.getValue()).append(')');
373                 }
374                 fingerprint.append(")");
375                 fingerprint.append(")");
376
377                 return fingerprint.toString();
378         }
379
380         /**
381          * Container for a profile field.
382          *
383          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
384          */
385         public class Field {
386
387                 /** The ID of the field. */
388                 private final String id;
389
390                 /** The name of the field. */
391                 private String name;
392
393                 /** The value of the field. */
394                 private String value;
395
396                 /**
397                  * Creates a new field with a random ID.
398                  */
399                 private Field() {
400                         this(UUID.randomUUID().toString());
401                 }
402
403                 /**
404                  * Creates a new field with the given ID.
405                  *
406                  * @param id
407                  *            The ID of the field
408                  */
409                 private Field(String id) {
410                         Validation.begin().isNotNull("Field ID", id).check();
411                         this.id = id;
412                 }
413
414                 /**
415                  * Returns the ID of this field.
416                  *
417                  * @return The ID of this field
418                  */
419                 public String getId() {
420                         return id;
421                 }
422
423                 /**
424                  * Returns the name of this field.
425                  *
426                  * @return The name of this field
427                  */
428                 public String getName() {
429                         return name;
430                 }
431
432                 /**
433                  * Sets the name of this field. The name must not be {@code null} and
434                  * must not match any other fields in this profile but my match the name
435                  * of this field.
436                  *
437                  * @param name
438                  *            The new name of this field
439                  * @return This field
440                  */
441                 public Field setName(String name) {
442                         Validation.begin().isNotNull("Field Name", name).check().is("Field Unique", (getFieldByName(name) == null) || equals(getFieldByName(name))).check();
443                         this.name = name;
444                         return this;
445                 }
446
447                 /**
448                  * Returns the value of this field.
449                  *
450                  * @return The value of this field
451                  */
452                 public String getValue() {
453                         return value;
454                 }
455
456                 /**
457                  * Sets the value of this field. While {@code null} is allowed, no
458                  * guarantees are made that {@code null} values are correctly persisted
459                  * across restarts of the plugin!
460                  *
461                  * @param value
462                  *            The new value of this field
463                  * @return This field
464                  */
465                 public Field setValue(String value) {
466                         this.value = value;
467                         return this;
468                 }
469
470                 //
471                 // OBJECT METHODS
472                 //
473
474                 /**
475                  * {@inheritDoc}
476                  */
477                 @Override
478                 public boolean equals(Object object) {
479                         if (!(object instanceof Field)) {
480                                 return false;
481                         }
482                         Field field = (Field) object;
483                         return id.equals(field.id);
484                 }
485
486                 /**
487                  * {@inheritDoc}
488                  */
489                 @Override
490                 public int hashCode() {
491                         return id.hashCode();
492                 }
493
494         }
495
496 }