/*
* Copyright 2012 Nodeable Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.streamreduce.core.model;
import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Indexed;
import com.google.code.morphia.annotations.Reference;
import com.google.code.morphia.annotations.Transient;
import com.streamreduce.util.HashtagUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.hibernate.validator.constraints.ScriptAssert;
@ScriptAssert.List({
@ScriptAssert(script = "_this instanceof com.streamreduce.core.model.User || _this.account != undefined",
lang = "javascript",
message = "account may not be null"), // Validate account for all non-User object since User validates itself
@ScriptAssert(script = "_this instanceof com.streamreduce.core.model.User || (_this.alias != undefined && _this.alias.trim().length() > 0)",
lang = "javascript",
message = "alias may not be empty"), // Validate alias for all non-User object since User validates itself
@ScriptAssert(script = "!(_this.updatedViaREST && _this.visibility.toString() == 'PUBLIC')",
lang = "javascript",
message = "visibility cannot be set to 'PUBLIC'") // Validate that visibility is not PUBLIC when updated via REST
})
public abstract class SobaObject extends ObjectWithId implements Taggable {
private static final long serialVersionUID = 5391473553602970543L;
@Indexed
// @NotEmpty <<< Handled by ScriptAssert above
@Size(max = 128, min = 1)
protected String alias;
@Transient
protected int DESCRIPTION_MAX = 512; // enforced on setter until the service layer can deal
protected String description;
@Indexed
@Reference
// @NotNull <<< Handled by ScriptAssert above
protected Account account;
@Indexed
@Reference
protected User user;
@NotNull
protected Visibility visibility = Visibility.ACCOUNT;
@Embedded
@NotNull
protected Set<String> hashtags = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
@Indexed
protected String externalId; // an optional external primary key, guid, etc..
public enum Visibility {
SELF,
GROUP,
ACCOUNT,
PUBLIC;
public int intValue() {
switch (this) {
case SELF:
return 1;
case GROUP:
return 10;
case ACCOUNT:
return 20;
case PUBLIC:
return 30;
default:
return 0;
}
}
}
@Override
public void mergeWithJSON(JSONObject json) {
super.mergeWithJSON(json);
if (json != null) {
if (json.containsKey("alias")) {
setAlias(json.getString("alias"));
}
if (json.containsKey("description")) {
setDescription(json.getString("description"));
}
if (json.containsKey("visibility")) {
setVisibility(Visibility.valueOf(json.getString("visibility")));
}
if (json.containsKey("hashtags")) {
setHashtags(new HashSet(json.getJSONArray("hashtags")));
}
}
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
if (description != null && description.length() > DESCRIPTION_MAX) {
description = description.substring(0, DESCRIPTION_MAX - 1);
}
this.description = description;
}
public Visibility getVisibility() {
return visibility;
}
public void setVisibility(Visibility visibility) {
if (visibility == null) {
throw new IllegalArgumentException("visibility can't be null");
}
this.visibility = visibility;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
if (StringUtils.isBlank(alias)) {
throw new IllegalArgumentException("alias can't be blank");
}
this.alias = alias;
}
@Override
public void addHashtags(Set<String> hashtags) {
setHashtags(hashtags);
}
@Override
public void addHashtag(String tag) {
if (tag != null && !tag.isEmpty()) {
tag = HashtagUtil.normalizeTag(tag);
if (!hashtags.contains(tag)) {
hashtags.add(tag);
}
}
}
@Override
public void removeHashtag(String tag) {
if (tag != null && !tag.isEmpty()) {
tag = HashtagUtil.normalizeTag(tag);
hashtags.remove(tag);
}
}
public Set<String> getHashtags() {
return hashtags;
}
public void setHashtags(Set<String> hashtags) {
if (hashtags != null) {
for (String tag : hashtags) {
addHashtag(tag);
}
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getExternalId() {
return externalId;
}
public void setExternalId(String externalId) {
this.externalId = externalId;
}
@SuppressWarnings("rawtypes")
public static abstract class Builder<T extends SobaObject, S extends Builder> {
protected T theObject;
/**
* Constructor.
*
* @param theObject instance of the actual object to create.
*/
public Builder(T theObject) {
this.theObject = theObject;
}
protected boolean isBuilt = false;
/**
* This method will return the real builder object instead of one of its parents.
*
* @return the actual buidler being used by the caller
*/
public abstract S getRealBuilder();
/**
* Builds the object and returns it. (Cannot be called more than once.)
*
* @return
*/
public T build() {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built");
}
if (theObject.getAccount() == null) {
throw new IllegalStateException("SobaObject must have an account set");
}
if (theObject.getUser() == null) {
throw new IllegalStateException("SobaObject must have a user set");
}
return theObject;
}
public S account(Account account) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setAccount(account);
return getRealBuilder();
}
/**
* @param user Defines the {@link User} and the {@link Account} for this Object.
* @return
*/
public S user(User user) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setUser(user);
theObject.setAccount(user.getAccount());
return getRealBuilder();
}
public S alias(String alias) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setAlias(alias);
return getRealBuilder();
}
public S description(String description) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setDescription(description);
return getRealBuilder();
}
@SuppressWarnings("unchecked")
public S hashtags(Set<String> hashtags) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setHashtags(hashtags);
return getRealBuilder();
}
public S hashtag(String hashtag) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.addHashtag(hashtag);
return getRealBuilder();
}
public S visibility(Visibility visibility) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
// if null use object default
if (visibility != null) {
theObject.setVisibility(visibility);
}
return getRealBuilder();
}
public S externalId(String externalId) {
if (isBuilt) {
throw new IllegalStateException("The object cannot be modified after built.");
}
theObject.setExternalId(externalId);
return getRealBuilder();
}
}
}