X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2FProfile.java;h=61cf2911f372e93aa33b329ee9a81e14353689af;hb=0a4b6fc252003c71f4bdef09560e87982838d9c8;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..61cf291 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,439 @@ 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 java.lang.Math.max; +import static java.lang.Math.min; +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; + } + + public String getFirstName() { + return name.getFirst().orNull(); + } + + public String getMiddleName() { + return name.getMiddle().orNull(); + } + + public String getLastName() { + return name.getLast().orNull(); + } + + public Integer getBirthDay() { + return birthDate.getDay().orNull(); + } + + public Integer getBirthMonth() { + return birthDate.getMonth().orNull(); + } + + public Integer getBirthYear() { + return birthDate.getYear().orNull(); + } + + public String getAvatar() { + return avatar; + } + + public Profile setAvatar(Optional avatarId) { + this.avatar = avatarId.orNull(); + return this; + } + + public List getFields() { + return new ArrayList(fields); + } + + 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(); + } + + public Optional getFieldByName(String fieldName) { + for (Field field : fields) { + if (field.getName().equals(fieldName)) { + return of(field); + } + } + return absent(); + } + + public Field addField(String fieldName) throws IllegalArgumentException { + checkNotNull(fieldName, "fieldName must not be null"); + checkArgument(fieldName.length() > 0, "fieldName must not be empty"); + checkArgument(!getFieldByName(fieldName).isPresent(), "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)); + } - /** The name of the user this profile belongs to. */ - private final String username; + public void moveFieldUp(Field field) { + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + int fieldIndex = getFieldIndex(field); + fields.remove(field); + fields.add(max(fieldIndex - 1, 0), field); + } + + public void moveFieldDown(Field field) { + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + int fieldIndex = getFieldIndex(field); + fields.remove(field); + fields.add(min(fieldIndex + 1, fields.size()), field); + } + + public void removeField(Field field) { + checkNotNull(field, "field must not be null"); + 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 + // /** - * Creates a new profile. + * Returns the index of the field with the given name. * - * @param username - * 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 Profile(String username) { - this.username = username; + private int getFieldIndex(Field field) { + return fields.indexOf(field); + } + + // + // INTERFACE Fingerprintable + // + + @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) { + if (field.getValue() != null) { + hash.putString(field.getName()).putString("(").putString(field.getValue()).putString(")"); + } + } + hash.putString(")"); + hash.putString(")"); + + return hash.hash().toString(); } /** - * Returns the name of the user this profile belongs to. + * Container for a profile field. * - * @return The name of the user this profile belongs to + * @author David ‘Bombe’ Roden */ - public String username() { - return username; + 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(); + } + } }