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, 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.ade.sitemap.shared; 029 030import org.opencms.ade.detailpage.CmsDetailPageInfo; 031import org.opencms.util.CmsUUID; 032 033import java.io.Serializable; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039import java.util.stream.Collectors; 040 041/** 042 * A data structure for managing the detail page ordering for different types in a given sitemap.<p> 043 * 044 * @since 8.0.0 045 */ 046public class CmsDetailPageTable implements Cloneable, Serializable { 047 048 /** A type indicating the status of a page. */ 049 public static enum Status { 050 /** default detail page. */ 051 firstDetailPage, 052 /** no detail page. */ 053 noDetailPage, 054 /** non-default detail page. */ 055 otherDetailPage 056 } 057 058 /** ID for serialization. */ 059 private static final long serialVersionUID = -4561142050519767250L; 060 061 /** The detail page info beans indexed by id. */ 062 private Map<CmsUUID, CmsDetailPageInfo> m_infoById = new HashMap<CmsUUID, CmsDetailPageInfo>(); 063 064 /** The detail page info beans, indexed by type. */ 065 private Map<String, List<CmsDetailPageInfo>> m_map = new HashMap<>(); 066 067 /** 068 * Creates a detail page table from a list of detail page info bean.<p> 069 * 070 * @param infos the detail page info beans 071 */ 072 public CmsDetailPageTable(List<CmsDetailPageInfo> infos) { 073 074 for (CmsDetailPageInfo info : infos) { 075 m_map.compute(info.getType(), (k, vs) -> vs == null ? new ArrayList<>() : vs).add(info); 076 m_infoById.put(info.getId(), info); 077 } 078 } 079 080 /** 081 * Empty default constructor for serialization.<p> 082 */ 083 protected CmsDetailPageTable() { 084 085 // for serialization 086 } 087 088 /** 089 * Adds a new detail page information bean to the detail page table.<p> 090 * 091 * @param info the detail page info to add 092 */ 093 public void add(CmsDetailPageInfo info) { 094 095 m_map.computeIfAbsent(info.getType(), type -> new ArrayList<>()).add(info); 096 m_infoById.put(info.getId(), info); 097 } 098 099 /** 100 * Checks if the entry for the given id can be made the default detail page entry for its type. 101 * 102 * @param id the id to check 103 * @return true if the entry can be made the default detail page entry 104 */ 105 public boolean canMakeDefault(CmsUUID id) { 106 107 if (isDefaultDetailPage(id)) { 108 return false; 109 } 110 CmsDetailPageInfo info = m_infoById.get(id); 111 if ((info == null) || info.isInherited()) { 112 return false; 113 } 114 return true; 115 } 116 117 /** 118 * Returns true if the detail page table contains a page with a given id.<p> 119 * 120 * @param id the page id 121 * @return true if the detail page table contains the page with the given id 122 */ 123 public boolean contains(CmsUUID id) { 124 125 return m_infoById.containsKey(id); 126 } 127 128 /** 129 * Copies the detail page table.<p> 130 * 131 * @return the copy of the detail page table 132 */ 133 public CmsDetailPageTable copy() { 134 135 List<CmsDetailPageInfo> infos = toList(); 136 CmsDetailPageTable result = new CmsDetailPageTable(); 137 for (CmsDetailPageInfo info : infos) { 138 result.add(info); 139 } 140 return result; 141 } 142 143 /** 144 * Returns the detail page info for a given page id.<p> 145 * 146 * @param id a page id 147 * @return the detail page info for the given page id 148 */ 149 public CmsDetailPageInfo get(CmsUUID id) { 150 151 return m_infoById.get(id); 152 } 153 154 /** 155 * Returns the page ids of all detail pages. <p> 156 * 157 * @return the page ids of all detail pages 158 */ 159 public Collection<CmsUUID> getAllIds() { 160 161 return m_infoById.keySet(); 162 } 163 164 /** 165 * Returns the list of detail page info beans for a given type.<p> 166 * 167 * @param type the type for which the detail page beans should be retrieved 168 * 169 * @return the detail page beans for that type 170 */ 171 public List<CmsDetailPageInfo> getInfosForType(String type) { 172 173 return new ArrayList<CmsDetailPageInfo>(m_map.computeIfAbsent(type, k -> new ArrayList<>())); 174 } 175 176 /** 177 * Returns the page status for the page with the given id.<p> 178 * 179 * @param id the id for which the page status should be checked 180 * 181 * @return the status of the page with the given id 182 */ 183 public Status getStatus(CmsUUID id) { 184 185 CmsDetailPageInfo info = m_infoById.get(id); 186 if (info == null) { 187 return Status.noDetailPage; 188 } 189 String type = info.getType(); 190 List<CmsDetailPageInfo> pagesWithNoQualifier = m_map.computeIfAbsent( 191 type, 192 k -> new ArrayList<>()).stream().filter(detailPage -> detailPage.getQualifier() == null).collect( 193 Collectors.toList()); 194 int index = pagesWithNoQualifier.indexOf(info); 195 // if info has a qualifier, index will be -1, but it's still in the list of other detail pages 196 if (index == 0) { 197 return Status.firstDetailPage; 198 } 199 return Status.otherDetailPage; 200 } 201 202 /** 203 * Returns true if the page with the given id is the default detail page for its type.<p> 204 * 205 * @param id a page id 206 * 207 * @return true if the detail page for the page id is the default detail page 208 */ 209 public boolean isDefaultDetailPage(CmsUUID id) { 210 211 CmsDetailPageInfo info = m_infoById.get(id); 212 if (info == null) { 213 return false; 214 } 215 CmsDetailPageInfo firstUnqualifiedEntry = m_map.get(info.getType()).stream().filter( 216 page -> page.getQualifier() == null).findFirst().orElse(null); 217 return (firstUnqualifiedEntry != null) && firstUnqualifiedEntry.getId().equals(id); 218 } 219 220 /** 221 * Moves the detail page information for a given page to the front of the detail pages for the same type.<p> 222 * 223 * @param id a page id 224 * 225 * @return the original position of the detail page entry in the list for the same type 226 */ 227 public int makeDefault(CmsUUID id) { 228 229 CmsDetailPageInfo info = m_infoById.get(id); 230 if (info == null) { 231 throw new IllegalArgumentException(); 232 } 233 234 CmsDetailPageInfo infoToSave = info; 235 // Making a detail page the default detail page should discard the qualifier 236 237 if (info.getQualifier() != null) { 238 CmsDetailPageInfo info2 = new CmsDetailPageInfo( 239 info.getId(), 240 info.getUri(), 241 info.getType(), 242 null, 243 info.getIconClasses()); 244 m_infoById.put(id, info2); 245 if (info.isInherited()) { 246 // this case should be prevented by the GUI, we handle it just to make sure the entry is not saved 247 infoToSave = info2.copyAsInherited(); 248 } else { 249 infoToSave = info2; 250 } 251 } 252 String type = info.getType(); 253 List<CmsDetailPageInfo> infos = m_map.computeIfAbsent(type, k -> new ArrayList<>()); 254 int oldPos = infos.indexOf(info); 255 infos.remove(oldPos); 256 infos.add(0, infoToSave); 257 return oldPos; 258 } 259 260 /** 261 * Removes the detail page with the given id.<p> 262 * 263 * @param id the id of the detail page to remove 264 * 265 * @return the original position of the detail page in the list for its type 266 */ 267 public int remove(CmsUUID id) { 268 269 CmsDetailPageInfo info = m_infoById.get(id); 270 if (info == null) { 271 throw new IllegalArgumentException(); 272 } 273 String type = info.getType(); 274 List<CmsDetailPageInfo> infos = m_map.get(type); 275 int pos = infos.indexOf(info); 276 infos.remove(pos); 277 m_infoById.remove(id); 278 return pos; 279 280 } 281 282 /** 283 * The number of configured detail pages.<p> 284 * 285 * @return the number of detail pages 286 */ 287 public int size() { 288 289 return m_infoById.size(); 290 } 291 292 /** 293 * Returns a flat list containing all detail pages for all types which preserves the order of detail pages from each type list.<p> 294 * 295 * @return a list of all detail page info beans 296 */ 297 public List<CmsDetailPageInfo> toList() { 298 299 List<CmsDetailPageInfo> result = new ArrayList<CmsDetailPageInfo>(); 300 for (String key : m_map.keySet()) { 301 for (CmsDetailPageInfo info : m_map.get(key)) { 302 result.add(info); 303 } 304 } 305 return result; 306 } 307 308}