X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2FProfile.java;h=6201276b8a03c3dafade2630fb7971b76e546039;hb=532076508aac8e03e0ef9914e90c7a0558b66bbe;hp=b1040b3f0bebc686769e37fe9a42d07c6ae01416;hpb=11059e54480499b3a173ca5e6db30de45cf15196;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/data/Profile.java b/src/main/java/net/pterodactylus/sone/data/Profile.java index b1040b3..6201276 100644 --- a/src/main/java/net/pterodactylus/sone/data/Profile.java +++ b/src/main/java/net/pterodactylus/sone/data/Profile.java @@ -1,5 +1,5 @@ /* - * FreenetSone - Profile.java - Copyright © 2010 David Roden + * Sone - Profile.java - Copyright © 2010–2013 David Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,33 +17,528 @@ package net.pterodactylus.sone.data; +import static com.google.common.base.Optional.absent; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Optional.of; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static java.util.UUID.randomUUID; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.google.common.base.Optional; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + /** - * A profile stores personal information about a {@link User}. + * A profile stores personal information about a {@link Sone}. All information + * is optional and can be {@code null}. * * @author David ‘Bombe’ Roden */ -public class Profile { +public class Profile implements Fingerprintable { + + /** The Sone this profile belongs to. */ + private final Sone sone; + + private volatile Name name = new Name(); + private volatile BirthDate birthDate = new BirthDate(); + + /** The ID of the avatar image. */ + private volatile String avatar; + + /** Additional fields in the profile. */ + private final List fields = Collections.synchronizedList(new ArrayList()); + + /** + * Creates a new empty profile. + * + * @param sone + * The Sone this profile belongs to + */ + public Profile(Sone sone) { + this.sone = sone; + } + + /** + * Creates a copy of a profile. + * + * @param profile + * The profile to copy + */ + public Profile(Profile profile) { + this.sone = profile.sone; + this.name = profile.name; + this.birthDate = profile.birthDate; + this.avatar = profile.avatar; + this.fields.addAll(profile.fields); + } + + // + // ACCESSORS + // + + /** + * Returns the Sone this profile belongs to. + * + * @return The Sone this profile belongs to + */ + public Sone getSone() { + return sone; + } + + /** + * Returns the first name. + * + * @return The first name + */ + public String getFirstName() { + return name.getFirst().orNull(); + } + + /** + * Returns the middle name(s). + * + * @return The middle name + */ + public String getMiddleName() { + return name.getMiddle().orNull(); + } + + /** + * Returns the last name. + * + * @return The last name + */ + public String getLastName() { + return name.getLast().orNull(); + } + + /** + * Returns the day of the birth date. + * + * @return The day of the birth date (from 1 to 31) + */ + public Integer getBirthDay() { + return birthDate.getDay().orNull(); + } + + /** + * Returns the month of the birth date. + * + * @return The month of the birth date (from 1 to 12) + */ + public Integer getBirthMonth() { + return birthDate.getMonth().orNull(); + } + + /** + * Returns the year of the birth date. + * + * @return The year of the birth date + */ + public Integer getBirthYear() { + return birthDate.getYear().orNull(); + } + + /** + * Returns the ID of the currently selected avatar image. + * + * @return The ID of the currently selected avatar image, or {@code null} if + * no avatar is selected. + */ + public String getAvatar() { + return avatar; + } + + /** + * Sets the avatar image. + * + * @param avatarId + * The ID of the new avatar image + * @return This profile + */ + public Profile setAvatar(Optional avatarId) { + this.avatar = avatarId.orNull(); + return this; + } + + /** + * Returns the fields of this profile. + * + * @return The fields of this profile + */ + public List getFields() { + return new ArrayList(fields); + } + + /** + * Returns whether this profile contains the given field. + * + * @param field + * The field to check for + * @return {@code true} if this profile contains the field, false otherwise + */ + public boolean hasField(Field field) { + return fields.contains(field); + } + + public Optional getFieldById(String fieldId) { + checkNotNull(fieldId, "fieldId must not be null"); + for (Field field : fields) { + if (field.getId().equals(fieldId)) { + return of(field); + } + } + return absent(); + } - /** The name of the user this profile belongs to. */ - private final String username; + public Optional getFieldByName(String fieldName) { + for (Field field : fields) { + if (field.getName().equals(fieldName)) { + return of(field); + } + } + return absent(); + } + + /** + * Appends a new field to the list of fields. + * + * @param fieldName + * The name of the new field + * @return The new field + * @throws IllegalArgumentException + * if the name is not valid + */ + public Field addField(String fieldName) throws IllegalArgumentException { + checkNotNull(fieldName, "fieldName must not be null"); + checkArgument(fieldName.length() > 0, "fieldName must not be empty"); + checkState(getFieldByName(fieldName) == null, "fieldName must be unique"); + @SuppressWarnings("synthetic-access") + Field field = new Field(fieldName); + fields.add(field); + return field; + } + + public void renameField(Field field, String newName) { + int indexOfField = getFieldIndex(field); + if (indexOfField == -1) { + return; + } + fields.set(indexOfField, new Field(field.getId(), newName, field.getValue())); + } + + public void setField(Field field, String newValue) { + int indexOfField = getFieldIndex(field); + if (indexOfField == -1) { + return; + } + fields.set(indexOfField, new Field(field.getId(), field.getName(), newValue)); + } + + /** + * Moves the given field up one position in the field list. The index of the + * field to move must be greater than {@code 0} (because you obviously can + * not move the first field further up). + * + * @param field + * The field to move up + */ + public void moveFieldUp(Field field) { + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + checkArgument(getFieldIndex(field) > 0, "field index must be > 0"); + int fieldIndex = getFieldIndex(field); + fields.remove(field); + fields.add(fieldIndex - 1, field); + } + + /** + * Moves the given field down one position in the field list. The index of + * the field to move must be less than the index of the last field (because + * you obviously can not move the last field further down). + * + * @param field + * The field to move down + */ + public void moveFieldDown(Field field) { + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + checkArgument(getFieldIndex(field) < fields.size() - 1, "field index must be < " + (fields.size() - 1)); + int fieldIndex = getFieldIndex(field); + fields.remove(field); + fields.add(fieldIndex + 1, field); + } /** - * Creates a new profile. + * Removes the given field. * - * @param username - * The name of the user this profile belongs to + * @param field + * The field to remove */ - public Profile(String username) { - this.username = username; + public void removeField(Field field) { + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + fields.remove(field); + } + + public Modifier modify() { + return new Modifier() { + private Optional firstName = name.getFirst(); + private Optional middleName = name.getMiddle(); + private Optional lastName = name.getLast(); + private Optional birthYear = birthDate.getYear(); + private Optional birthMonth = birthDate.getMonth(); + private Optional birthDay = birthDate.getDay(); + + @Override + public Modifier setFirstName(String firstName) { + this.firstName = fromNullable(firstName); + return this; + } + + @Override + public Modifier setMiddleName(String middleName) { + this.middleName = fromNullable(middleName); + return this; + } + + @Override + public Modifier setLastName(String lastName) { + this.lastName = fromNullable(lastName); + return this; + } + + @Override + public Modifier setBirthYear(Integer birthYear) { + this.birthYear = fromNullable(birthYear); + return this; + } + + @Override + public Modifier setBirthMonth(Integer birthMonth) { + this.birthMonth = fromNullable(birthMonth); + return this; + } + + @Override + public Modifier setBirthDay(Integer birthDay) { + this.birthDay = fromNullable(birthDay); + return this; + } + + @Override + public Profile update() { + Profile.this.name = new Name(firstName, middleName, lastName); + Profile.this.birthDate = new BirthDate(birthYear, birthMonth, birthDay); + return Profile.this; + } + }; } + public interface Modifier { + + Modifier setFirstName(String firstName); + Modifier setMiddleName(String middleName); + Modifier setLastName(String lastName); + Modifier setBirthYear(Integer birthYear); + Modifier setBirthMonth(Integer birthMonth); + Modifier setBirthDay(Integer birthDay); + Profile update(); + + } + + // + // PRIVATE METHODS + // + /** - * Returns the name of the user this profile belongs to. + * Returns the index of the field with the given name. * - * @return The name of the user this profile belongs to + * @param field + * The name of the field + * @return The index of the field, or {@code -1} if there is no field with + * the given name */ - public String username() { - return username; + private int getFieldIndex(Field field) { + return fields.indexOf(field); + } + + // + // INTERFACE Fingerprintable + // + + /** + * {@inheritDoc} + */ + @Override + public String getFingerprint() { + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Profile("); + hash.putString(name.getFingerprint()); + hash.putString(birthDate.getFingerprint()); + if (avatar != null) { + hash.putString("Avatar(").putString(avatar).putString(")"); + } + hash.putString("ContactInformation("); + for (Field field : fields) { + hash.putString(field.getName()).putString("(").putString(field.getValue()).putString(")"); + } + hash.putString(")"); + hash.putString(")"); + + return hash.hash().toString(); + } + + /** + * Container for a profile field. + * + * @author David ‘Bombe’ Roden + */ + public static class Field { + + private final String id; + private final String name; + private final String value; + + public Field(String name) { + this(name, null); + } + + public Field(String name, String value) { + this(randomUUID().toString(), name, value); + } + + public Field(String id, String name, String value) { + this.id = checkNotNull(id, "id must not be null"); + this.name = name; + this.value = value; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Field)) { + return false; + } + Field field = (Field) object; + return id.equals(field.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + } + + public static class Name implements Fingerprintable { + + private final Optional first; + private final Optional middle; + private final Optional last; + + public Name() { + this(Optional.absent(), Optional.absent(), Optional.absent()); + } + + public Name(Optional first, Optional middle, Optional last) { + this.first = first; + this.middle = middle; + this.last = last; + } + + public Optional getFirst() { + return first; + } + + public Optional getMiddle() { + return middle; + } + + public Optional getLast() { + return last; + } + + @Override + public String getFingerprint() { + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Name("); + if (first.isPresent()) { + hash.putString("First(").putString(first.get()).putString(")"); + } + if (middle.isPresent()) { + hash.putString("Middle(").putString(middle.get()).putString(")"); + } + if (last.isPresent()) { + hash.putString("Last(").putString(last.get()).putString(")"); + } + hash.putString(")"); + return hash.hash().toString(); + } + + } + + public static class BirthDate implements Fingerprintable { + + private final Optional year; + private final Optional month; + private final Optional day; + + public BirthDate() { + this(Optional.absent(), Optional.absent(), Optional.absent()); + } + + public BirthDate(Optional year, Optional month, Optional day) { + this.year = year; + this.month = month; + this.day = day; + } + + public Optional getYear() { + return year; + } + + public Optional getMonth() { + return month; + } + + public Optional getDay() { + return day; + } + + @Override + public String getFingerprint() { + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Birthdate("); + if (year.isPresent()) { + hash.putString("Year(").putInt(year.get()).putString(")"); + } + if (month.isPresent()) { + hash.putString("Month(").putInt(month.get()).putString(")"); + } + if (day.isPresent()) { + hash.putString("Day(").putInt(day.get()).putString(")"); + } + hash.putString(")"); + return hash.hash().toString(); + } + } }