001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bstefanescu 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.webengine.ui.tree; 021 022import org.nuxeo.common.utils.Path; 023 024/** 025 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 026 */ 027public class TreeItemImpl implements TreeItem { 028 029 private static final long serialVersionUID = 5252830785508229998L; 030 031 public static final int F_CONTAINER = 4; 032 033 public static final int F_EXPANDED = 8; 034 035 public static final TreeItem[] EMPTY_CHILDREN = new TreeItem[0]; 036 037 public static final TreeItem[] HAS_CHILDREN = new TreeItem[0]; 038 039 protected final ContentProvider provider; 040 041 protected final TreeItem parent; 042 043 protected final Path path; 044 045 protected String label; 046 047 protected String[] facets; 048 049 protected TreeItem[] children = EMPTY_CHILDREN; 050 051 protected final Object obj; 052 053 protected volatile int state = BOTH; 054 055 // TODO: use a map? 056 // protected Map<String, TreeItem> childrenMap; 057 058 public TreeItemImpl(TreeItem parent, ContentProvider provider, Object data) { 059 this.parent = parent; 060 this.provider = provider; 061 obj = data; 062 String name = provider.getName(obj); 063 if (parent != null) { 064 path = parent.getPath().append(name); 065 } else { 066 path = new Path("/"); 067 } 068 if (provider.isContainer(obj)) { 069 state |= F_CONTAINER; // set container flag and invalidate children 070 } 071 } 072 073 public TreeItemImpl(ContentProvider provider, Object data) { 074 this(null, provider, data); 075 } 076 077 public TreeItemImpl(TreeItem parent, Object data) { 078 this(parent, parent.getContentProvider(), data); 079 } 080 081 public boolean hasChildren() { 082 return children.length > 0; 083 } 084 085 public TreeItem[] getChildren() { 086 validateChildren(); 087 return children; 088 } 089 090 public Object getObject() { 091 return obj; 092 } 093 094 public Path getPath() { 095 return path; 096 } 097 098 public TreeItem getParent() { 099 return parent; 100 } 101 102 public ContentProvider getContentProvider() { 103 return provider; 104 } 105 106 public String getName() { 107 return path.lastSegment(); 108 } 109 110 public String getLabel() { 111 validateData(); 112 return label; 113 } 114 115 public String[] getFacets() { 116 validateData(); 117 return facets; 118 } 119 120 public boolean isContainer() { 121 return (state & F_CONTAINER) != 0; 122 } 123 124 public TreeItem find(Path path) { 125 TreeItem item = this; 126 for (int i = 0, len = path.segmentCount() - 1; i < len; i++) { 127 if (!item.hasChildren()) { 128 return null; 129 } 130 item = item.getChild(path.segment(i)); 131 if (item == null) { 132 return null; 133 } 134 } 135 if (!item.hasChildren()) { 136 return null; 137 } 138 return item.getChild(path.lastSegment()); 139 } 140 141 public TreeItem findAndReveal(Path path) { 142 // we expand only parents and not the last segment 143 TreeItem item = this; 144 int len = path.segmentCount(); 145 for (int i = 0; i < len; i++) { 146 item.expand(); 147 item = item.getChild(path.segment(i)); 148 if (item == null) { 149 return null; 150 } 151 } 152 return item; 153 } 154 155 public TreeItem getChild(String name) { 156 validateChildren(); 157 return _getChild(name); 158 } 159 160 protected TreeItem _getChild(String name) { 161 for (TreeItem child : children) { 162 if (name.equals(child.getName())) { 163 return child; 164 } 165 } 166 return null; 167 } 168 169 public TreeItem[] expand() { 170 if (isExpanded()) { 171 return children; 172 } else { 173 if (parent != null && !parent.isExpanded()) { 174 parent.expand(); 175 } 176 state |= F_EXPANDED; 177 return getChildren(); 178 } 179 } 180 181 protected void loadData() { 182 label = provider.getLabel(obj); 183 facets = provider.getFacets(obj); 184 } 185 186 public void validateData() { 187 if ((state & DATA) != 0) { 188 loadData(); 189 state &= ~DATA; 190 } 191 } 192 193 public void validateChildren() { 194 if ((state & CHILDREN) != 0) { 195 loadChildren(); 196 state &= ~CHILDREN; 197 } 198 } 199 200 protected void loadChildren() { 201 if (!isContainer()) { 202 return; 203 } 204 Object[] objects = parent == null ? provider.getElements(obj) : provider.getChildren(obj); 205 if (objects == null) { 206 children = null; 207 } else { 208 children = new TreeItemImpl[objects.length]; 209 for (int i = 0; i < objects.length; i++) { 210 children[i] = new TreeItemImpl(this, objects[i]); 211 } 212 } 213 } 214 215 public void collapse() { 216 state &= ~F_EXPANDED; 217 } 218 219 public boolean isExpanded() { 220 return (state & F_EXPANDED) != 0; 221 } 222 223 /* 224 * TODO not completely implemented 225 */ 226 public void refresh(int type) { 227 if ((type & DATA) != 0) { 228 loadData(); 229 } 230 if ((type & CHILDREN) != 0) { 231 loadChildren(); 232 } 233 state &= ~type; 234 } 235 236 public void validate() { 237 refresh(state); 238 } 239 240 public void invalidate(int type) { 241 state |= type; 242 } 243 244 /* 245 * TODO not implemented 246 */ 247 public int getValidationState() { 248 return state; 249 } 250 251 public Object accept(TreeItemVisitor visitor) { 252 return visitor.visit(this); 253 } 254 255 @Override 256 public String toString() { 257 return "TreeItem: " + obj.toString(); 258 } 259 260 @Override 261 public boolean equals(Object obj) { 262 if (obj == this) { 263 return true; 264 } 265 if (obj instanceof TreeItem) { 266 return getObject().equals(((TreeItem) obj).getObject()); 267 } 268 return false; 269 } 270 271}