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