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 public boolean hasChildren() { 084 return children.length > 0; 085 } 086 087 public TreeItem[] getChildren() { 088 validateChildren(); 089 return children; 090 } 091 092 public Object getObject() { 093 return obj; 094 } 095 096 public Path getPath() { 097 return path; 098 } 099 100 public TreeItem getParent() { 101 return parent; 102 } 103 104 public ContentProvider getContentProvider() { 105 return provider; 106 } 107 108 public String getName() { 109 return path.lastSegment(); 110 } 111 112 public String getLabel() { 113 validateData(); 114 return label; 115 } 116 117 public String[] getFacets() { 118 validateData(); 119 return facets; 120 } 121 122 public boolean isContainer() { 123 return (state & F_CONTAINER) != 0; 124 } 125 126 public TreeItem find(Path path) { 127 TreeItem item = this; 128 for (int i = 0, len = path.segmentCount() - 1; i < len; i++) { 129 if (!item.hasChildren()) { 130 return null; 131 } 132 item = item.getChild(path.segment(i)); 133 if (item == null) { 134 return null; 135 } 136 } 137 if (!item.hasChildren()) { 138 return null; 139 } 140 return item.getChild(path.lastSegment()); 141 } 142 143 public TreeItem findAndReveal(Path path) { 144 // we expand only parents and not the last segment 145 TreeItem item = this; 146 int len = path.segmentCount(); 147 for (int i = 0; i < len; i++) { 148 item.expand(); 149 item = item.getChild(path.segment(i)); 150 if (item == null) { 151 return null; 152 } 153 } 154 return item; 155 } 156 157 public TreeItem getChild(String name) { 158 validateChildren(); 159 return _getChild(name); 160 } 161 162 protected TreeItem _getChild(String name) { 163 for (TreeItem child : children) { 164 if (name.equals(child.getName())) { 165 return child; 166 } 167 } 168 return null; 169 } 170 171 public TreeItem[] expand() { 172 if (isExpanded()) { 173 return children; 174 } else { 175 if (parent != null && !parent.isExpanded()) { 176 parent.expand(); 177 } 178 state |= F_EXPANDED; 179 return getChildren(); 180 } 181 } 182 183 protected void loadData() { 184 label = provider.getLabel(obj); 185 facets = provider.getFacets(obj); 186 } 187 188 public void validateData() { 189 if ((state & DATA) != 0) { 190 loadData(); 191 state &= ~DATA; 192 } 193 } 194 195 public void validateChildren() { 196 if ((state & CHILDREN) != 0) { 197 loadChildren(); 198 state &= ~CHILDREN; 199 } 200 } 201 202 protected void loadChildren() { 203 if (!isContainer()) { 204 return; 205 } 206 Object[] objects = parent == null ? provider.getElements(obj) : provider.getChildren(obj); 207 if (objects == null) { 208 children = null; 209 } else { 210 children = new TreeItemImpl[objects.length]; 211 for (int i = 0; i < objects.length; i++) { 212 children[i] = new TreeItemImpl(this, objects[i]); 213 } 214 } 215 } 216 217 public void collapse() { 218 state &= ~F_EXPANDED; 219 } 220 221 public boolean isExpanded() { 222 return (state & F_EXPANDED) != 0; 223 } 224 225 /* 226 * TODO not completely implemented 227 */ 228 public void refresh(int type) { 229 if ((type & DATA) != 0) { 230 loadData(); 231 } 232 if ((type & CHILDREN) != 0) { 233 loadChildren(); 234 } 235 state &= ~type; 236 } 237 238 public void validate() { 239 refresh(state); 240 } 241 242 public void invalidate(int type) { 243 state |= type; 244 } 245 246 /* 247 * TODO not implemented 248 */ 249 public int getValidationState() { 250 return state; 251 } 252 253 public Object accept(TreeItemVisitor visitor) { 254 return visitor.visit(this); 255 } 256 257 @Override 258 public String toString() { 259 return "TreeItem: " + obj.toString(); 260 } 261 262 @Override 263 public boolean equals(Object obj) { 264 if (obj == this) { 265 return true; 266 } 267 if (obj instanceof TreeItem) { 268 return getObject().equals(((TreeItem) obj).getObject()); 269 } 270 return false; 271 } 272 273}