Remove modified tracker from Profile.
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Profile.java
1 /*
2  * FreenetSone - 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.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import net.pterodactylus.util.validation.Validation;
27
28 /**
29  * A profile stores personal information about a {@link Sone}. All information
30  * is optional and can be {@code null}.
31  *
32  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
33  */
34 public class Profile implements Fingerprintable {
35
36         /** The first name. */
37         private volatile String firstName;
38
39         /** The middle name(s). */
40         private volatile String middleName;
41
42         /** The last name. */
43         private volatile String lastName;
44
45         /** The day of the birth date. */
46         private volatile Integer birthDay;
47
48         /** The month of the birth date. */
49         private volatile Integer birthMonth;
50
51         /** The year of the birth date. */
52         private volatile Integer birthYear;
53
54         /** Additional fields in the profile. */
55         private final List<String> fields = Collections.synchronizedList(new ArrayList<String>());
56
57         /** The field values. */
58         private final Map<String, String> fieldValues = Collections.synchronizedMap(new HashMap<String, String>());
59
60         /**
61          * Creates a new empty profile.
62          */
63         public Profile() {
64                 /* do nothing. */
65         }
66
67         /**
68          * Creates a copy of a profile.
69          *
70          * @param profile
71          *            The profile to copy
72          */
73         public Profile(Profile profile) {
74                 if (profile == null) {
75                         return;
76                 }
77                 this.firstName = profile.firstName;
78                 this.middleName = profile.middleName;
79                 this.lastName = profile.lastName;
80                 this.birthDay = profile.birthDay;
81                 this.birthMonth = profile.birthMonth;
82                 this.birthYear = profile.birthYear;
83                 this.fieldValues.putAll(profile.fieldValues);
84         }
85
86         //
87         // ACCESSORS
88         //
89
90         /**
91          * Returns the first name.
92          *
93          * @return The first name
94          */
95         public String getFirstName() {
96                 return firstName;
97         }
98
99         /**
100          * Sets the first name.
101          *
102          * @param firstName
103          *            The first name to set
104          * @return This profile (for method chaining)
105          */
106         public Profile setFirstName(String firstName) {
107                 this.firstName = firstName;
108                 return this;
109         }
110
111         /**
112          * Returns the middle name(s).
113          *
114          * @return The middle name
115          */
116         public String getMiddleName() {
117                 return middleName;
118         }
119
120         /**
121          * Sets the middle name.
122          *
123          * @param middleName
124          *            The middle name to set
125          * @return This profile (for method chaining)
126          */
127         public Profile setMiddleName(String middleName) {
128                 this.middleName = middleName;
129                 return this;
130         }
131
132         /**
133          * Returns the last name.
134          *
135          * @return The last name
136          */
137         public String getLastName() {
138                 return lastName;
139         }
140
141         /**
142          * Sets the last name.
143          *
144          * @param lastName
145          *            The last name to set
146          * @return This profile (for method chaining)
147          */
148         public Profile setLastName(String lastName) {
149                 this.lastName = lastName;
150                 return this;
151         }
152
153         /**
154          * Returns the day of the birth date.
155          *
156          * @return The day of the birth date (from 1 to 31)
157          */
158         public Integer getBirthDay() {
159                 return birthDay;
160         }
161
162         /**
163          * Sets the day of the birth date.
164          *
165          * @param birthDay
166          *            The day of the birth date (from 1 to 31)
167          * @return This profile (for method chaining)
168          */
169         public Profile setBirthDay(Integer birthDay) {
170                 this.birthDay = birthDay;
171                 return this;
172         }
173
174         /**
175          * Returns the month of the birth date.
176          *
177          * @return The month of the birth date (from 1 to 12)
178          */
179         public Integer getBirthMonth() {
180                 return birthMonth;
181         }
182
183         /**
184          * Sets the month of the birth date.
185          *
186          * @param birthMonth
187          *            The month of the birth date (from 1 to 12)
188          * @return This profile (for method chaining)
189          */
190         public Profile setBirthMonth(Integer birthMonth) {
191                 this.birthMonth = birthMonth;
192                 return this;
193         }
194
195         /**
196          * Returns the year of the birth date.
197          *
198          * @return The year of the birth date
199          */
200         public Integer getBirthYear() {
201                 return birthYear;
202         }
203
204         /**
205          * Sets the year of the birth date.
206          *
207          * @param birthYear
208          *            The year of the birth date
209          * @return This profile (for method chaining)
210          */
211         public Profile setBirthYear(Integer birthYear) {
212                 this.birthYear = birthYear;
213                 return this;
214         }
215
216         /**
217          * Appends a new field to the list of fields.
218          *
219          * @param field
220          *            The field to add
221          * @throws IllegalArgumentException
222          *             if the name is not valid
223          */
224         public void addField(String field) throws IllegalArgumentException {
225                 Validation.begin().isNotNull("Field Name", field).check().isGreater("Field Name Length", field.length(), 0).isEqual("Field Name Unique", !fields.contains(field), true).check();
226                 fields.add(field);
227         }
228
229         /**
230          * Moves the field with the given index up one position in the field list.
231          * The index of the field to move must be greater than {@code 0} (because
232          * you obviously can not move the first field further up).
233          *
234          * @param fieldIndex
235          *            The index of the field to move
236          */
237         public void moveFieldUp(int fieldIndex) {
238                 Validation.begin().isGreater("Field Index", fieldIndex, 0).isLess("Field Index", fieldIndex, fields.size()).check();
239                 String field = fields.remove(fieldIndex);
240                 fields.add(fieldIndex - 1, field);
241         }
242
243         /**
244          * Moves the field with the given name up one position in the field list.
245          * The field must not be the first field (because you obviously can not move
246          * the first field further up).
247          *
248          * @param field
249          *            The name of the field to move
250          */
251         public void moveFieldUp(String field) {
252                 Validation.begin().isNotNull("Field Name", field).check().isGreater("Field Name Length", field.length(), 0).isEqual("Field Name Existing", fields.contains(field), true).check();
253                 moveFieldUp(getFieldIndex(field));
254         }
255
256         /**
257          * Moves the field with the given index down one position in the field list.
258          * The index of the field to move must be less than the index of the last
259          * field (because you obviously can not move the last field further down).
260          *
261          * @param fieldIndex
262          *            The index of the field to move
263          */
264         public void moveFieldDown(int fieldIndex) {
265                 Validation.begin().isGreaterOrEqual("Field Index", fieldIndex, 0).isLess("Field Index", fieldIndex, fields.size() - 1).check();
266                 String field = fields.remove(fieldIndex);
267                 fields.add(fieldIndex + 1, field);
268         }
269
270         /**
271          * Moves the field with the given name down one position in the field list.
272          * The field must not be the last field (because you obviously can not move
273          * the last field further down).
274          *
275          * @param field
276          *            The name of the field to move
277          */
278         public void moveFieldDown(String field) {
279                 Validation.begin().isNotNull("Field Name", field).check().isGreater("Field Name Length", field.length(), 0).isEqual("Field Name Existing", fields.contains(field), true).check();
280                 moveFieldDown(getFieldIndex(field));
281         }
282
283         /**
284          * Removes the field at the given index.
285          *
286          * @param fieldIndex
287          *            The index of the field to remove
288          */
289         public void removeField(int fieldIndex) {
290                 Validation.begin().isGreaterOrEqual("Field Index", fieldIndex, 0).isLess("Field Index", fieldIndex, fields.size()).check();
291                 String field = fields.remove(fieldIndex);
292                 fieldValues.remove(field);
293         }
294
295         /**
296          * Removes the field with the given name.
297          *
298          * @param field
299          *            The name of the field
300          */
301         public void removeField(String field) {
302                 Validation.begin().isNotNull("Field Name", field).check().isGreater("Field Name Length", field.length(), 0).isEqual("Field Name Existing", fields.contains(field), true).check();
303                 removeField(getFieldIndex(field));
304         }
305
306         /**
307          * Returns the value of the field with the given name.
308          *
309          * @param field
310          *            The name of the field
311          * @return The value of the field, or {@code null} if there is no such field
312          */
313         public String getField(String field) {
314                 return fieldValues.get(field);
315         }
316
317         /**
318          * Sets the value of the field with the given name.
319          *
320          * @param field
321          *            The name of the field
322          * @param value
323          *            The value of the field
324          */
325         public void setField(String field, String value) {
326                 Validation.begin().isNotNull("Field Name", field).check().isGreater("Field Name Length", field.length(), 0).isEqual("Field Name Existing", fields.contains(field), true).check();
327                 fieldValues.put(field, value);
328         }
329
330         /**
331          * Returns a list of all fields stored in this profile.
332          *
333          * @return The fields of this profile
334          */
335         public List<String> getFields() {
336                 return Collections.unmodifiableList(fields);
337         }
338
339         //
340         // PRIVATE METHODS
341         //
342
343         /**
344          * Returns the index of the field with the given name.
345          *
346          * @param field
347          *            The name of the field
348          * @return The index of the field, or {@code -1} if there is no field with
349          *         the given name
350          */
351         private int getFieldIndex(String field) {
352                 return fields.indexOf(field);
353         }
354
355         //
356         // INTERFACE Fingerprintable
357         //
358
359         /**
360          * {@inheritDoc}
361          */
362         @Override
363         public String getFingerprint() {
364                 StringBuilder fingerprint = new StringBuilder();
365                 fingerprint.append("Profile(");
366                 if (firstName != null) {
367                         fingerprint.append("FirstName(").append(firstName).append(')');
368                 }
369                 if (middleName != null) {
370                         fingerprint.append("MiddleName(").append(middleName).append(')');
371                 }
372                 if (lastName != null) {
373                         fingerprint.append("LastName(").append(lastName).append(')');
374                 }
375                 if (birthDay != null) {
376                         fingerprint.append("BirthDay(").append(birthDay).append(')');
377                 }
378                 if (birthMonth != null) {
379                         fingerprint.append("BirthMonth(").append(birthMonth).append(')');
380                 }
381                 if (birthYear != null) {
382                         fingerprint.append("BirthYear(").append(birthYear).append(')');
383                 }
384                 fingerprint.append("ContactInformation(");
385                 for (String field : fields) {
386                         fingerprint.append(field).append('(').append(fieldValues.get(field)).append(')');
387                 }
388                 fingerprint.append(")");
389                 fingerprint.append(")");
390
391                 return fingerprint.toString();
392         }
393
394 }