package models.origo.core;
import origo.helpers.UIElementHelper;
import play.data.validation.Required;
import play.db.jpa.Model;
import play.modules.origo.core.Node;
import play.modules.origo.core.ui.UIElement;
import javax.persistence.*;
import java.util.*;
@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "nodeVersion", columnNames = {"nodeId", "version"}))
public final class RootNode extends Model implements Node {
@Required
public String nodeId;
// TODO: Should only have to be Integer but because of defect #521 in play that doesn't work. Should be fixed in 1.3 (2.0?)
@Required
public Long version;
@Temporal(value = TemporalType.TIMESTAMP)
public Date publish;
@Temporal(value = TemporalType.TIMESTAMP)
public Date unPublish;
public String type;
public String themeVariant;
/**
* Only kept to make sure all elements added to the HEAD region are unique (we don't want duplicate javascript or css resources.)
*/
@Transient
private Map<String, UIElement> headElement = new HashMap<String, UIElement>();
/**
* A list of UIElements for each region on the page. The key is the region name.
*/
@Transient
private Map<String, List<UIElement>> uiElements = new HashMap<String, List<UIElement>>();
public RootNode(Long version) {
this(UUID.randomUUID().toString(), version);
}
public RootNode(String nodeId, Long version) {
this.nodeId = nodeId;
this.version = version;
}
@Override
public String getTitle() {
return toString();
}
@Override
public String getNodeId() {
return nodeId;
}
@Override
public Long getVersion() {
return version;
}
@Override
public Date getDatePublished() {
return publish;
}
@Override
public Date getDateUnpublished() {
return unPublish;
}
@Override
public String getThemeVariant() {
return themeVariant;
}
@Override
public Set<String> getRegions() {
return this.uiElements.keySet();
}
/* Interface methods */
@Override
public List<UIElement> getUIElements(String region) {
return this.uiElements.get(region.toLowerCase());
}
@Override
public UIElement addHeadUIElement(UIElement uiElement) {
return addHeadUIElement(uiElement, false);
}
@Override
public UIElement addUIElement(UIElement uiElement) {
return addUIElement(uiElement, false);
}
@Override
public UIElement addHeadUIElement(UIElement uiElement, boolean reorderElementsBelow) {
String elementKey = String.valueOf(uiElement.hashCode());
if (headElement.containsKey(elementKey)) {
return headElement.get(elementKey);
} else {
headElement.put(elementKey, uiElement);
return addUIElement(uiElement, reorderElementsBelow, HEAD, uiElement.getWeight());
}
}
@Override
public UIElement addUIElement(UIElement uiElement, boolean reorderElementsBelow) {
Meta meta = Meta.findWithNodeIdAndSpecificVersion(nodeId, version, uiElement.id);
if (meta == null) {
meta = Meta.defaultMeta();
}
String regionKey = meta.region.toLowerCase();
return addUIElement(uiElement, reorderElementsBelow, regionKey, meta.weight.intValue());
}
private UIElement addUIElement(UIElement uiElement, boolean reorderElementsBelow, String regionKey, int weight) {
if (!uiElements.containsKey(regionKey)) {
uiElements.put(regionKey, new ArrayList<UIElement>());
}
uiElement.setWeight(weight);
uiElements.get(regionKey).add(uiElement);
if (reorderElementsBelow) {
UIElementHelper.repositionUIElements(uiElements.get(regionKey), uiElement);
}
UIElementHelper.reorderUIElements(uiElements.get(regionKey));
return uiElement;
}
@Override
public boolean removeHeadUIElement(UIElement uiElement) {
return removeUIElement(uiElement, HEAD);
}
@Override
public boolean removeUIElement(UIElement uiElement) {
Meta meta = Meta.findWithNodeIdAndSpecificVersion(nodeId, version, uiElement.id);
if (meta == null) {
meta = Meta.defaultMeta();
}
String regionKey = meta.region.toLowerCase();
return removeUIElement(uiElement, regionKey);
}
private boolean removeUIElement(UIElement uiElement, String regionKey) {
if (uiElements.get(regionKey).remove(uiElement)) {
UIElementHelper.reorderUIElements(uiElements.get(regionKey));
return true;
}
return false;
}
public static List<RootNode> findAllCurrentVersions(Date today) {
List<RootNode> leaves = RootNode.find(
"select l from RootNode l " +
"where l.version = (" +
"select max(l2.version) from RootNode l2 " +
"where l2.nodeId = l.nodeId and " +
"(l2.publish = null or l2.publish < :today) and " +
"(l2.unPublish = null or l2.unPublish >= :today)" +
")"
).bind("today", today).fetch();
for (RootNode node : leaves) {
initializeNode(node);
}
return leaves;
}
public static RootNode findWithNodeIdAndSpecificVersion(String nodeId, Long version) {
RootNode node = RootNode.find(
"select distinct n from RootNode n " +
"where n.nodeId = :nodeId and " +
"n.version = :version"
).bind("nodeId", nodeId).bind("version", version).first();
if (node != null) {
initializeNode(node);
}
return node;
}
public static RootNode findLatestPublishedVersionWithNodeId(String nodeId, Date today) {
RootNode node = RootNode.find(
"select distinct n from RootNode n " +
"where n.nodeId = :nodeId and " +
"(n.publish = null or n.publish < :today) and " +
"(n.unPublish = null OR n.unPublish >= :today)" +
"order by version desc"
).bind("nodeId", nodeId).bind("today", today).first();
if (node != null) {
initializeNode(node);
}
return node;
}
public static RootNode findLatestVersionWithNodeId(String nodeId) {
RootNode node = RootNode.find(
"select distinct n from RootNode n " +
"where n.nodeId = :nodeId " +
"order by version desc"
).bind("nodeId", nodeId).first();
if (node != null) {
initializeNode(node);
}
return node;
}
public static List<RootNode> findAllVersionsWithNodeId(String nodeId) {
List<RootNode> leaves = RootNode.find(
"select distinct n from RootNode n where n.nodeId = :nodeId"
).bind("nodeId", nodeId).fetch();
for (RootNode node : leaves) {
initializeNode(node);
}
return leaves;
}
private static void initializeNode(RootNode node) {
node.uiElements = new HashMap<String, List<UIElement>>();
node.uiElements.put(HEAD, new ArrayList<UIElement>());
node.headElement = new HashMap<String, UIElement>();
}
@Override
public String toString() {
return "Node (" + nodeId + "," + version + ")";
}
public RootNode copy() {
return copy(false);
}
public RootNode copy(boolean increaseVersion) {
RootNode copy = new RootNode(nodeId, increaseVersion ? ++version : version);
copy.publish = publish;
copy.unPublish = unPublish;
copy.type = type;
copy.themeVariant = themeVariant;
return copy;
}
}