001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.workplace.tools; 029 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.Iterator; 034import java.util.List; 035import java.util.Map; 036 037/** 038 * Default implementation of a named object container. <p> 039 * 040 * It can handle relative or absolute orderings and unique names.<p> 041 * 042 * @param <T> the type of objects 043 * 044 * @since 6.0.0 045 */ 046public class CmsIdentifiableObjectContainer<T> { 047 048 /** 049 * Internal class just for taking care of the positions in the container.<p> 050 * 051 * @param <T> the object type 052 * 053 * @since 6.0.0 054 */ 055 private static class CmsIdObjectElement<T> { 056 057 /** Identifiable object. */ 058 private final T m_object; 059 060 /** Relative position. */ 061 private final float m_position; 062 063 /** 064 * Default Constructor.<p> 065 * 066 * @param object the object 067 * @param position the relative position 068 * 069 */ 070 public CmsIdObjectElement(T object, float position) { 071 072 m_object = object; 073 m_position = position; 074 } 075 076 /** 077 * Returns the object.<p> 078 * 079 * @return the object 080 */ 081 public T getObject() { 082 083 return m_object; 084 } 085 086 /** 087 * Returns the position.<p> 088 * 089 * @return the position 090 */ 091 public float getPosition() { 092 093 return m_position; 094 } 095 096 } 097 098 /** Cache for element list. */ 099 private List<T> m_cache; 100 101 /** List of objects. */ 102 private final List<T> m_objectList = new ArrayList<T>(); 103 104 /** Map of objects only used if uniqueIds flag set. */ 105 private final Map<String, T> m_objectsById = new HashMap<String, T>(); 106 107 /** Map of object lists by id. */ 108 private final Map<String, List<T>> m_objectsListsById = new HashMap<String, List<T>>(); 109 110 /** List of ordered objects. */ 111 private final List<CmsIdObjectElement<T>> m_orderedObjectList = new ArrayList<CmsIdObjectElement<T>>(); 112 113 /** Flag for managing absolute and relative ordering. */ 114 private final boolean m_relativeOrdered; 115 116 /** Flag for managing uniqueness check. */ 117 private final boolean m_uniqueIds; 118 119 /** 120 * Default Constructor.<p> 121 * 122 * @param uniqueIds if the list show check for unique ids 123 * @param relativeOrdered if the list show use relative ordering, instead of absolute ordering 124 */ 125 public CmsIdentifiableObjectContainer(boolean uniqueIds, boolean relativeOrdered) { 126 127 m_uniqueIds = uniqueIds; 128 m_relativeOrdered = relativeOrdered; 129 } 130 131 /** 132 * Appends the specified object to the end of this container. <p> 133 * 134 * @param id the object identifier 135 * @param idObject the object add to the container 136 * 137 * @see java.util.List#add(Object) 138 */ 139 public void addIdentifiableObject(String id, T idObject) { 140 141 m_cache = null; 142 if (m_uniqueIds && (m_objectsById.get(id) != null)) { 143 removeObject(id); 144 } 145 if (m_relativeOrdered) { 146 float pos = 1; 147 if (!m_orderedObjectList.isEmpty()) { 148 pos = m_orderedObjectList.get(m_orderedObjectList.size() - 1).getPosition() + 1; 149 } 150 m_orderedObjectList.add(new CmsIdObjectElement<T>(idObject, pos)); 151 } else { 152 m_objectList.add(idObject); 153 } 154 if (m_uniqueIds) { 155 m_objectsById.put(id, idObject); 156 } else { 157 List<T> prevObj = m_objectsListsById.get(id); 158 if (prevObj == null) { 159 List<T> list = new ArrayList<T>(); 160 list.add(idObject); 161 m_objectsListsById.put(id, list); 162 } else { 163 prevObj.add(idObject); 164 } 165 } 166 167 } 168 169 /** 170 * Inserts the specified object at the specified position in this container.<p> 171 * 172 * Shifts the object currently at that position (if any) and any subsequent 173 * objects to the right (adds one to their indices).<p> 174 * 175 * @param id the object identifier 176 * @param idObject the object add to the container 177 * @param position the insertion point 178 * 179 * @see java.util.List#add(int, Object) 180 */ 181 public void addIdentifiableObject(String id, T idObject, float position) { 182 183 m_cache = null; 184 if (m_uniqueIds && (m_objectsById.get(id) != null)) { 185 removeObject(id); 186 } 187 if (m_relativeOrdered) { 188 int pos = 0; 189 Iterator<CmsIdObjectElement<T>> itElems = m_orderedObjectList.iterator(); 190 while (itElems.hasNext()) { 191 CmsIdObjectElement<T> element = itElems.next(); 192 if (element.getPosition() > position) { 193 break; 194 } 195 pos++; 196 } 197 m_orderedObjectList.add(pos, new CmsIdObjectElement<T>(idObject, position)); 198 } else { 199 m_objectList.add((int)position, idObject); 200 } 201 if (m_uniqueIds) { 202 m_objectsById.put(id, idObject); 203 } else { 204 List<T> prevObj = m_objectsListsById.get(id); 205 if (prevObj == null) { 206 List<T> list = new ArrayList<T>(); 207 list.add(idObject); 208 m_objectsListsById.put(id, list); 209 } else { 210 prevObj.add(idObject); 211 } 212 } 213 214 } 215 216 /** 217 * Resets the container.<p> 218 */ 219 public void clear() { 220 221 m_cache = null; 222 m_objectList.clear(); 223 m_objectsById.clear(); 224 m_orderedObjectList.clear(); 225 m_objectsListsById.clear(); 226 } 227 228 /** 229 * Returns the list of objects.<p> 230 * 231 * @return the a list of <code>{@link Object}</code>s. 232 */ 233 public List<T> elementList() { 234 235 if (m_cache != null) { 236 return m_cache; 237 } 238 if (m_relativeOrdered) { 239 List<T> objectList = new ArrayList<T>(); 240 Iterator<CmsIdObjectElement<T>> itObjs = m_orderedObjectList.iterator(); 241 while (itObjs.hasNext()) { 242 CmsIdObjectElement<T> object = itObjs.next(); 243 objectList.add(object.getObject()); 244 } 245 m_cache = Collections.unmodifiableList(objectList); 246 } else { 247 m_cache = Collections.unmodifiableList(m_objectList); 248 } 249 return m_cache; 250 } 251 252 /** 253 * Returns the object with the given id.<p> 254 * 255 * If <code>uniqueIds</code> is set to <code>false</code> an <code>{@link Object}</code> 256 * containing a <code>{@link List}</code> with all the objects with the given id is returned.<p> 257 * 258 * If the container no contains any object with the given id, <code>null</code> is returned.<p> 259 * 260 * @param id the id of the object 261 * 262 * @return the object if found, or <code>null</code> 263 * 264 * @see java.util.Map#get(Object) 265 */ 266 public T getObject(String id) { 267 268 if (!m_uniqueIds) { 269 throw new UnsupportedOperationException("Not supported for not unique ids"); 270 } 271 return m_objectsById.get(id); 272 273 } 274 275 /** 276 * Returns the list of objects with the given id.<p> 277 * 278 * @param id the object id 279 * 280 * @return the list of objects if found, or <code>null</code> 281 */ 282 public List<T> getObjectList(String id) { 283 284 if (m_uniqueIds) { 285 throw new UnsupportedOperationException("Not supported for unique ids"); 286 } 287 return m_objectsListsById.get(id); 288 } 289 290 /** 291 * Removes an object with the given id.<p> 292 * 293 * if <code>m_uniqueIds</code> is set, it will remove at most one object. 294 * otherwise it will remove all elements with the given id.<p> 295 * 296 * @param id the id of the object to remove 297 */ 298 public synchronized void removeObject(String id) { 299 300 m_cache = null; 301 if (m_relativeOrdered) { 302 if (m_uniqueIds) { 303 Object o = getObject(id); 304 Iterator<CmsIdObjectElement<T>> itObjs = m_orderedObjectList.iterator(); 305 while (itObjs.hasNext()) { 306 CmsIdObjectElement<T> object = itObjs.next(); 307 if (object.getObject() == o) { 308 itObjs.remove(); 309 break; 310 } 311 } 312 m_objectsById.remove(id); 313 } else { 314 Iterator<T> itRemove = m_objectsListsById.get(id).iterator(); 315 while (itRemove.hasNext()) { 316 T o = itRemove.next(); 317 Iterator<CmsIdObjectElement<T>> itObjs = m_orderedObjectList.iterator(); 318 while (itObjs.hasNext()) { 319 CmsIdObjectElement<T> object = itObjs.next(); 320 if (object.getObject() == o) { 321 itObjs.remove(); 322 break; 323 } 324 } 325 } 326 m_orderedObjectList.remove(id); 327 } 328 } else { 329 Object o = getObject(id); 330 m_objectList.remove(o); 331 if (m_uniqueIds) { 332 m_objectsById.remove(id); 333 } else { 334 m_objectsListsById.remove(id); 335 } 336 } 337 } 338}