001/* 002 * Copyright 2010 Anyware Services 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 */ 016package org.ametys.plugins.odfweb.repository; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.NoSuchElementException; 025import java.util.Map.Entry; 026import java.util.Objects; 027import java.util.Optional; 028import java.util.Set; 029import java.util.stream.Collectors; 030 031import org.ametys.cms.FilterNameHelper; 032import org.ametys.odf.program.Program; 033import org.ametys.plugins.explorer.resources.ResourceCollection; 034import org.ametys.plugins.repository.AmetysObject; 035import org.ametys.plugins.repository.AmetysObjectIterable; 036import org.ametys.plugins.repository.AmetysObjectIterator; 037import org.ametys.plugins.repository.AmetysRepositoryException; 038import org.ametys.plugins.repository.CollectionIterable; 039import org.ametys.plugins.repository.UnknownAmetysObjectException; 040import org.ametys.plugins.repository.metadata.CompositeMetadata; 041import org.ametys.web.repository.page.Page; 042import org.ametys.web.repository.page.UnknownZoneException; 043import org.ametys.web.repository.page.Zone; 044import org.ametys.web.repository.site.Site; 045import org.ametys.web.repository.sitemap.Sitemap; 046 047/** 048 * Page representing a first-level page. 049 */ 050public class FirstLevelPage implements Page 051{ 052 private FirstLevelPageFactory _factory; 053 054 private Page _root; 055 private String _name; 056 private String _title; 057 private OdfPageHandler _odfPageHandler; 058 059 /** 060 * Constructor. 061 * @param factory the corresponding factory 062 * @param root the odf root page. 063 * @param name the page's name. 064 * @param title the page's title. 065 * @param odfPageHandler the ODF page handler. 066 */ 067 public FirstLevelPage(FirstLevelPageFactory factory, Page root, String name, String title, OdfPageHandler odfPageHandler) 068 { 069 _factory = factory; 070 _root = root; 071 _name = name; 072 _title = title; 073 _odfPageHandler = odfPageHandler; 074 } 075 076 @Override 077 public int getDepth() throws AmetysRepositoryException 078 { 079 return _root.getDepth() + 1; 080 } 081 082 @Override 083 public Set<String> getReferers() throws AmetysRepositoryException 084 { 085 return null; 086 } 087 088 @Override 089 public ResourceCollection getRootAttachments() throws AmetysRepositoryException 090 { 091 return null; 092 } 093 094 @Override 095 public String getTemplate() throws AmetysRepositoryException 096 { 097 // TODO 098 return "program-list"; 099 } 100 101 @Override 102 public String getTitle() throws AmetysRepositoryException 103 { 104 return _title; 105 } 106 107 @Override 108 public String getLongTitle() throws AmetysRepositoryException 109 { 110 return _title; 111 } 112 113 @Override 114 public PageType getType() throws AmetysRepositoryException 115 { 116 return PageType.CONTAINER; 117 } 118 119 @Override 120 public String getURL() throws AmetysRepositoryException 121 { 122 throw new UnsupportedOperationException("getURL not supported on virtual odf pages"); 123 } 124 125 @Override 126 public LinkType getURLType() throws AmetysRepositoryException 127 { 128 throw new UnsupportedOperationException("getURLType not supported on virtual odf pages"); 129 } 130 131 @Override 132 public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException 133 { 134 if (!"default".equals(name)) 135 { 136 throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual program pages."); 137 } 138 139 return new FirstLevelZone(this); 140 } 141 142 @Override 143 public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException 144 { 145 ArrayList<Zone> zones = new ArrayList<>(); 146 zones.add(new FirstLevelZone(this)); 147 return new CollectionIterable<>(zones); 148 } 149 150 @Override 151 public boolean hasZone(String name) throws AmetysRepositoryException 152 { 153 return "default".equals(name); 154 } 155 156 @Override 157 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 158 { 159 Map<String, String> secondLevelValues = _odfPageHandler.getLevel2Values(_root); 160 Map<String, Collection<Program>> secondLevelCache = Optional.ofNullable(_factory.getODFPageCache().getProgramCache(_root, true).get(_name)).orElseGet(HashMap::new); 161 162 List<Page> children = secondLevelCache.keySet().stream() 163 .map(secondLevelCode -> _findSecondLevelValueEntry(secondLevelValues, secondLevelCode).orElse(null)) 164 .filter(Objects::nonNull) 165 .map(entry -> _toSecondLevelPage(entry)) 166 .collect(Collectors.toList()); 167 168 return new CollectionIterable<>(children); 169 } 170 171 private Optional<Entry<String, String>> _findSecondLevelValueEntry(String levelCode) 172 { 173 Map<String, String> secondLevelValues = _odfPageHandler.getLevel2Values(_root); 174 return _findSecondLevelValueEntry(secondLevelValues, levelCode); 175 } 176 177 private Optional<Entry<String, String>> _findSecondLevelValueEntry(Map<String, String> secondLevelValues, String levelCode) 178 { 179 return secondLevelValues.entrySet().stream() 180 // entry = (code, title) 181 .filter(entry -> entry.getKey().equals(levelCode)) 182 .findFirst(); 183 } 184 185 private SecondLevelPage _toSecondLevelPage(Map.Entry<String, String> secondLevelValueEntry) 186 { 187 String childName = secondLevelValueEntry.getKey(); 188 String title = secondLevelValueEntry.getValue(); 189 190 return new SecondLevelPage(_factory.getResolver(), _factory.getODFPageCache(), _root, _name, childName, title, _odfPageHandler, this); 191 } 192 193 @Override 194 public String getPathInSitemap() throws AmetysRepositoryException 195 { 196 return _root.getPathInSitemap() + "/" + getName(); 197 } 198 199 @Override 200 public Site getSite() throws AmetysRepositoryException 201 { 202 return _root.getSite(); 203 } 204 205 @Override 206 public String getSiteName() throws AmetysRepositoryException 207 { 208 return _root.getSiteName(); 209 } 210 211 @Override 212 public Sitemap getSitemap() throws AmetysRepositoryException 213 { 214 return _root.getSitemap(); 215 } 216 217 @Override 218 public String getSitemapName() throws AmetysRepositoryException 219 { 220 return _root.getSitemapName(); 221 } 222 223 @SuppressWarnings("unchecked") 224 @Override 225 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 226 { 227 if (path.isEmpty()) 228 { 229 throw new AmetysRepositoryException("path must be non empty"); 230 } 231 232 int i = path.indexOf('/'); 233 234 String name = i == -1 ? path : path.substring(0, path.indexOf('/')); 235 236 int index = name.lastIndexOf("-"); 237 String level2Code = index != -1 ? _odfPageHandler.decodeLevelValue(name.substring(index + 1)) : _odfPageHandler.decodeLevelValue(name); 238 239 Page level2Page = _findSecondLevelValueEntry(level2Code) 240 .map(this::_toSecondLevelPage) 241 .orElseThrow(() -> 242 new UnknownAmetysObjectException("There's no virtual child page at path " + path + " for page " + _name)); 243 244 if (i == -1) 245 { 246 return (A) level2Page; 247 } 248 else 249 { 250 return (A) level2Page.getChild(path.substring(i + 1)); 251 } 252 } 253 254 @SuppressWarnings("unchecked") 255 @Override 256 public AmetysObjectIterable<? extends Page> getChildren() throws AmetysRepositoryException 257 { 258 return getChildrenPages(); 259 } 260 261 @Override 262 public boolean hasChild(String childName) throws AmetysRepositoryException 263 { 264 // FIXME should use page cache, because can return true when page does 265 // not exist. Maybe done like this for performance reasons? 266 Map<String, String> secondLevelValues = _odfPageHandler.getLevel2Values(_root); 267 return secondLevelValues.containsKey(childName); 268 } 269 270 @Override 271 public String getId() throws AmetysRepositoryException 272 { 273 // E.g: odfLevel1://XA?rootId=... 274 return "odfLevel1://" + _odfPageHandler.encodeLevelValue(_name) + "?rootId=" + _root.getId(); 275 } 276 277 @Override 278 public String getName() throws AmetysRepositoryException 279 { 280 // E.g: licence-lmd-XA 281 return FilterNameHelper.filterName(_title) + "-" + _odfPageHandler.encodeLevelValue(_name); 282 } 283 284 @SuppressWarnings("unchecked") 285 @Override 286 public Page getParent() throws AmetysRepositoryException 287 { 288 return _root; 289 } 290 291 @Override 292 public String getParentPath() throws AmetysRepositoryException 293 { 294 return _root.getPath(); 295 } 296 297 @Override 298 public String getPath() throws AmetysRepositoryException 299 { 300 return getParentPath() + "/" + getName(); 301 } 302 303 @Override 304 public CompositeMetadata getMetadataHolder() 305 { 306 return new StaticCompositeMetadata(); 307 } 308 309 @Override 310 public Set<String> getTags() throws AmetysRepositoryException 311 { 312 return Collections.emptySet(); 313 } 314 315 @Override 316 public boolean isVisible() throws AmetysRepositoryException 317 { 318 return true; 319 } 320 321 @Override 322 public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePages) throws AmetysRepositoryException 323 { 324 return getChildrenPages(); 325 } 326 327 @Override 328 public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException 329 { 330 if (index < 0) 331 { 332 throw new AmetysRepositoryException("Child page index cannot be negative"); 333 } 334 335 AmetysObjectIterator<? extends Page> childPages = getChildrenPages().iterator(); 336 337 try 338 { 339 childPages.skip(index); 340 return childPages.next(); 341 } 342 catch (NoSuchElementException e) 343 { 344 throw new UnknownAmetysObjectException("There's no child page at index " + index + " for page " + this.getId()); 345 } 346 } 347}