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