/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.entities.ads; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import nl.strohalm.cyclos.entities.Entity; import nl.strohalm.cyclos.entities.Relationship; import org.apache.commons.collections.CollectionUtils; /** * A hierarchical ad category * @author luis */ public class AdCategory extends Entity implements Comparable<AdCategory> { public static enum Relationships implements Relationship { PARENT("parent"), CHILDREN("children"); private final String name; private Relationships(final String name) { this.name = name; } @Override public String getName() { return name; } } /** * The maximum level for nesting categories<br> * Note: take into account the calculations in getGlobalOrder when increasing MAX_LEVEL. MAX_LEVEL cannot be increased infinitely. */ public static final int MAX_LEVEL = 3; private static final long serialVersionUID = -4371587757348684782L; private String name; private AdCategory parent; private Collection<AdCategory> children; private boolean active; private Integer order = 0; private BigInteger globalOrder; @Override public int compareTo(final AdCategory other) { return getFullName().compareTo(other.getFullName()); } public Collection<AdCategory> getChildren() { return children; } public String getFullName() { if (parent == null) { return name; } else { return parent.getFullName() + ": " + name; } } public String getFullNameButRoot() { if (getLevel() <= 2) { return name; } else { return parent.getFullNameButRoot() + ": " + name; } } /** * returns a BigInteger indicating the global order index of the category. In the global order, whenever an item with children is encountered, all * child nodes are first handled before going on with the next item/node on the same level.<br> * Note that this method only works correctly with a maximum of 999 subcategories for a certain category. * @author rinke */ public BigInteger getGlobalOrder() { if (globalOrder == null) { final int correctedLevel = AdCategory.MAX_LEVEL - getLevel(); final BigInteger levelFactor = new BigInteger("1000").pow(correctedLevel); // specified indexes are always > 0. Unspecified indexes of 0 mess up the calculation, so... final Integer index = (getOrder() == 0) ? 1 : getOrder(); final BigInteger parentGlobalOrder = (getParent() == null) ? BigInteger.ZERO : getParent().getGlobalOrder(); globalOrder = parentGlobalOrder.add(levelFactor.multiply(new BigInteger(index.toString()))); } return globalOrder; } public int getLevel() { if (parent == null) { return 1; } return 1 + parent.getLevel(); } @Override public String getName() { return name; } public Integer getOrder() { return order; } public AdCategory getParent() { return parent; } /** * Returns a list, beggining of the level 1 category to this one */ public List<AdCategory> getPathFromRoot() { final List<AdCategory> path = new ArrayList<AdCategory>(); AdCategory current = this; while (current != null) { path.add(current); current = current.getParent(); } Collections.reverse(path); return path; } public AdCategory getRootCategory() { if (parent == null) { return this; } return parent.getRootCategory(); } /** * tests if the category is disabled or not. Note that it does not test this for parent categories. If any parent category is disabled, then * logically the child categories are also disabled, even if this is not always updated to these child categories. For this reason, in particular * cases it's better to use {@link #isEnabled()}. * @return true if the category is not disabled. */ public boolean isActive() { return active; } /** * tests if this category or any of its parents is active. If a parent category is disabled, then logically the child categories are also * disabled, even though these might not be updated. For this reason, this method might be more appropriate than {@link #isActive()}. * @return true if and only if this category and all of it's parents in line are active. */ public boolean isEnabled() { AdCategory current = this; while (current != null) { if (!current.isActive()) { return false; } current = current.getParent(); } return true; } public boolean isLeaf() { return CollectionUtils.isEmpty(children); } public boolean isRoot() { return parent == null; } public void setActive(final boolean active) { this.active = active; globalOrder = null; } public void setChildren(final Collection<AdCategory> children) { this.children = children; } public void setName(final String name) { this.name = name; } public void setOrder(final Integer orderIndex) { order = orderIndex; globalOrder = null; } public void setParent(final AdCategory parent) { this.parent = parent; globalOrder = null; } @Override public String toString() { return getId() + " - " + name; } }