Dépôt officiel du code source de l'ERP OpenConcerto
Rev 156 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.group;
import org.openconcerto.utils.Tuple2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Allow to customize UI layout.
*
* @author guillaume
*/
public class Group extends Item {
public static Group copy(final Group g, final Group newParent) {
return new Group(g, newParent);
}
private static final Comparator<Tuple2<Item, Integer>> COMPARATOR = new Comparator<Tuple2<Item, Integer>>() {
@Override
public int compare(final Tuple2<Item, Integer> o1, final Tuple2<Item, Integer> o2) {
int c = o1.get1().compareTo(o2.get1());
if (c == 0) {
c = o1.get0().getId().compareTo(o2.get0().getId());
}
return c;
}
};
private int order;
private final List<Tuple2<Item, Integer>> list;
private String tabId;
public Group(final String id) {
this(id, LayoutHints.DEFAULT_GROUP_HINTS);
}
public Group(final String id, final LayoutHints hint) {
super(id, hint);
this.order = 100;
this.list = new ArrayList<Tuple2<Item, Integer>>();
}
public Group(final Group g, final Group newParent) {
super(g, newParent);
this.order = g.order;
this.list = new ArrayList<Tuple2<Item, Integer>>(g.list.size());
for (final Tuple2<Item, Integer> t : g.list) {
final Item copy = Item.copy(t.get0(), this);
this.list.add(new Tuple2<Item, Integer>(copy, t.get1()));
}
}
public final Group toImmutable() {
return this.toImmutable(true);
}
public final Group toImmutable(final boolean onlyDesc) {
if (this.isFrozen())
return this;
final Group res;
if (onlyDesc) {
res = copy(this, null);
res.freeze();
} else {
final List<String> p = this.getAbsolutePath();
final Group copy = copy(this.getRoot(), null);
copy.freeze();
res = copy.followPath(p, false);
}
assert res.isFrozen();
return res;
}
@Override
protected synchronized void _freeze() {
super._freeze();
for (final Tuple2<Item, Integer> child : this.list) {
child.get0()._freeze();
}
}
public Item addItem(final String string) {
final Item res = new Item(string);
this.add(res);
return res;
}
public Item addItem(final String string, final LayoutHints hint) {
final Item res = new Item(string, hint);
this.add(res);
return res;
}
public void add(final Item item) {
add(item, this.order);
}
public void add(final Item item, final int order) {
checkFrozen("add");
item.setParent(this);
this.list.add(new Tuple2<Item, Integer>(item, order));
Collections.sort(this.list, COMPARATOR);
if (this.order <= order) {
this.order = (order / 100) * 100 + 100;
}
}
public final int getSize() {
return this.list.size();
}
public final boolean isEmpty() {
return this.list.isEmpty();
}
public Item getItem(final int index) {
return this.list.get(index).get0();
}
public Integer getOrder(final int index) {
return this.list.get(index).get1();
}
public boolean contains(final String id) {
return getDescFromID(id) != null;
}
public final Collection<Group> getDescendantGroups() {
final List<Group> res = new ArrayList<Group>();
this.getDescendantGroups(res);
return res;
}
protected void getDescendantGroups(final Collection<Group> res) {
for (final Tuple2<Item, Integer> t : this.list) {
if (t.get0() instanceof Group) {
Group g = (Group) t.get0();
res.add(g);
g.getDescendantGroups(res);
}
}
}
@Override
public final Collection<Item> getDescendantItems() {
final List<Item> res = new ArrayList<Item>();
this.getDescendantItems(res);
return res;
}
@Override
protected void getDescendantItems(final Collection<Item> res) {
for (final Tuple2<Item, Integer> t : this.list) {
t.get0().getDescendantItems(res);
}
}
@Override
public Item getDescFromID(final String id, final int maxLevel) {
final Item res = super.getDescFromID(id, maxLevel);
if (res != null || maxLevel == 0)
return res;
final int size = this.getSize();
final int nextLevel = maxLevel < 0 ? maxLevel : maxLevel - 1;
for (int i = 0; i < size; i++) {
final Item desc = this.getItem(i).getDescFromID(id, nextLevel);
if (desc != null) {
return desc;
}
}
return null;
}
/**
* Get a descendant group.
*
* @param path a list of IDs.
* @param create <code>true</code> if missing descendant should be created.
* @return the descendant, or <code>null</code> if <code>create</code> is <code>false</code> and
* the descendant is missing.
*/
public final Group followPath(final List<String> path, final boolean create) {
final int size = path.size();
Group g = this;
for (int i = 0; i < size; i++) {
final String id = path.get(i);
final Item child = g.getChildFromID(id);
if (child instanceof Group) {
g = (Group) child;
} else if (child != null) {
throw new IllegalStateException("ID exists but isn't a group : " + child);
} else if (create) {
final Group ng = new Group(id);
g.add(ng);
g = ng;
} else {
return null;
}
}
return g;
}
public void remove(final String itemId) {
this.remove(this, itemId);
}
private void remove(final Group group, final String id) {
checkFrozen("remove");
final int size = group.getSize();
for (int i = 0; i < size; i++) {
final Item b = group.getItem(i);
if (b.getId().equals(id)) {
this.list.remove(i);
return;
}
if (b instanceof Group) {
remove((Group) b, id);
}
}
}
public Integer getOrder(final String id) {
final int size = this.getSize();
for (int i = 0; i < size; i++) {
final Tuple2<Item, Integer> b = this.list.get(i);
if (b.get0().getId().equals(id)) {
return b.get1();
}
}
return null;
}
public int getIndex(final String id) {
final int size = this.getSize();
for (int i = 0; i < size; i++) {
final Tuple2<Item, Integer> b = this.list.get(i);
if (b.get0().getId().equals(id)) {
return i;
}
}
return -1;
}
public String printTree() {
final StringBuilder b = new StringBuilder();
printTree(b, 0, 1);
return b.toString();
}
@Override
protected void printTree(final StringBuilder builder, final int localOrder, final int level) {
for (int i = 0; i < level - 1; i++) {
builder.append(" ");
}
builder.append("+-+ ");
builder.append(localOrder + " " + this.getId() + " [" + this.getLocalHint() + "]\n");
for (final Tuple2<Item, Integer> tuple : this.list) {
tuple.get0().printTree(builder, tuple.get1(), level + 1);
}
}
public String printTwoColumns() {
final StringBuilder b = new StringBuilder("==== Group " + this.getId() + " ====\n");
printColumns(b, 2, 0, 0, 1);
return b.toString();
}
@Override
protected int printColumns(final StringBuilder builder, final int width, int x, final int localOrder, final int level) {
if (this.getLocalHint().isSeparated()) {
x = 0;
builder.append(" -------\n");
}
if (isEmpty()) {
// print a leaf
x = super.printColumns(builder, width, x, localOrder, level);
} else {
// Subgroup
for (final Tuple2<Item, Integer> tuple : this.list) {
final Item subGroup = tuple.get0();
final Integer subGroupOrder = tuple.get1();
x = subGroup.printColumns(builder, width, x, subGroupOrder, level + 1);
}
}
return x;
}
@Override
public boolean equalsDesc(Object obj) {
if (this == obj)
return true;
if (!super.equalsDesc(obj))
return false;
if (getClass() != obj.getClass())
return false;
final Group other = (Group) obj;
final int size = this.list.size();
if (size != other.list.size())
return false;
for (int i = 0; i < size; i++) {
final Tuple2<Item, Integer> thisChild = this.list.get(i);
final Tuple2<Item, Integer> oChild = other.list.get(i);
if (!thisChild.get1().equals(oChild.get1()) || !thisChild.get0().equalsDesc(oChild.get0()))
return false;
}
return true;
}
public void setTabId(String tabId) {
this.tabId = tabId;
}
public String getTabId() {
return this.tabId;
}
}