package marubinotto.piggydb.model.entity;
import static marubinotto.util.CollectionUtils.set;
import java.util.HashSet;
import java.util.Set;
import marubinotto.piggydb.model.Tag;
import marubinotto.piggydb.model.TagRepository;
import marubinotto.piggydb.model.auth.User;
import marubinotto.piggydb.model.exception.AuthorizationException;
import marubinotto.piggydb.model.exception.InvalidTagNameException;
import marubinotto.util.Assert;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
public class RawTag extends RawClassifiable implements Tag {
protected final static int MIN_LENGTH = 2;
protected final static int MAX_LENGTH = 50;
protected final static String INVALID_CHARS = "\\";
private String name;
private Long popularity;
private Long fragmentId;
public RawTag() {
}
public RawTag(String name) {
setName(name);
}
public RawTag(String name, User user) {
super(user);
validateName(name);
ensureCanUse(new RawTag(name), user);
setName(name);
onPropertyChange(user);
}
private void validateName(String name) {
if (StringUtils.containsAny(name, INVALID_CHARS))
throw new InvalidTagNameException("invalid-tag-chars", INVALID_CHARS);
if (name.length() < MIN_LENGTH)
throw new InvalidTagNameException("tag-minlength-error", String.valueOf(MIN_LENGTH));
if (name.length() > MAX_LENGTH)
throw new InvalidTagNameException("tag-maxlength-error", String.valueOf(MAX_LENGTH));
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setNameByUser(String name, User user) {
Assert.Arg.notNull(name, "name");
Assert.Arg.notNull(user, "user");
if (ObjectUtils.equals(name, getName())) return;
validateName(name);
ensureCanChange(user);
ensureCanUse(new RawTag(name), user); // rename to
setName(name);
onPropertyChange(user);
}
public boolean isClassifiedAs(String name) {
Assert.Arg.notNull(name, "name");
if (name.equals(getName()) || getClassification().isSubordinateOf(name)) {
return true;
}
else {
return false;
}
}
public Long getPopularity() {
return this.popularity;
}
public void setPopularity(Long popularity) {
this.popularity = popularity;
}
public void addPopularity() {
this.popularity++;
}
public Set<Long> expandToIdsOfSubtree(TagRepository tagRepository)
throws Exception {
Assert.Arg.notNull(tagRepository, "tagRepository");
Assert.Property.requireNotNull(getId(), "id");
Set<Long> ids = new HashSet<Long>();
ids.add(getId());
ids.addAll(tagRepository.getAllSubordinateTagIds(set(getId())));
return ids;
}
public boolean isTrashTag() {
return NAME_TRASH.equals(getName());
}
public Long getFragmentId() {
return this.fragmentId;
}
public void setFragmentId(Long fragmentId) {
this.fragmentId = fragmentId;
}
public boolean isTagFragment() {
return this.fragmentId != null;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(getName()).toHashCode();
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Tag)) {
return false;
}
String targetName = ((Tag)object).getName();
if (targetName == null && getName() == null) return true;
if (targetName == null) return false;
if (getName() == null) return false;
return targetName.equals(getName());
}
@Override
public String toString() {
return getId() + ":" + getName();
}
//
// Authorization
//
public static boolean canUse(Tag tag, User user) {
Assert.Arg.notNull(tag, "tag");
Assert.Arg.notNull(user, "user");
try { ensureCanUse(tag, user); return true; }
catch (AuthorizationException e) { return false; }
}
private static final String CODE_NO_AUTH_FOR_TAG = "no-auth-for-tag";
public static void ensureCanUse(Tag tag, User user) {
if (user.isViewer()) {
throw new AuthorizationException(CODE_NO_AUTH_FOR_TAG, tag.getName());
}
if (tag.isClassifiedAs(Tag.NAME_USER) ||
tag.isClassifiedAs(Tag.NAME_PUBLIC) ||
tag.isClassifiedAs(Tag.NAME_BOOKMARK)) {
if (!user.isOwner())
throw new AuthorizationException(CODE_NO_AUTH_FOR_TAG, tag.getName());
}
}
public boolean authorizes(User user) {
return canUse(this, user);
}
public final boolean canRename(User user) {
Assert.Arg.notNull(user, "user");
try { ensureCanChange(user); return true; }
catch (AuthorizationException e) { return false; }
}
@Override
public void ensureCanChange(User user) throws AuthorizationException {
super.ensureCanChange(user);
ensureCanUse(this, user);
}
@Override
protected void ensureCanAddTag(Tag tag, User user) throws AuthorizationException {
super.ensureCanAddTag(tag, user);
if (!user.isOwner() && tag.isClassifiedAs(Tag.NAME_TRASH))
throw new AuthorizationException("no-auth-to-extend-trash");
}
@Override
public void ensureCanDelete(User user) throws AuthorizationException {
super.ensureCanDelete(user);
ensureCanUse(this, user);
}
}