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.Collection; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023import java.util.Objects; 024import java.util.Optional; 025import java.util.stream.Collectors; 026 027import org.apache.commons.lang3.StringUtils; 028 029import org.ametys.odf.program.Program; 030import org.ametys.odf.tree.OdfClassificationHandler.LevelValue; 031import org.ametys.plugins.repository.AmetysObject; 032import org.ametys.plugins.repository.AmetysObjectIterable; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.CollectionIterable; 035import org.ametys.plugins.repository.UnknownAmetysObjectException; 036import org.ametys.plugins.repository.jcr.NameHelper; 037import org.ametys.web.repository.page.Page; 038import org.ametys.web.repository.page.virtual.VirtualPageConfiguration; 039 040/** 041 * Page representing a first-level page. 042 */ 043public class FirstLevelPage extends AbstractOdfPage<FirstLevelPageFactory> 044{ 045 private String _name; 046 private String _title; 047 048 /** 049 * Constructor. 050 * @param factory the corresponding factory 051 * @param root the odf root page. 052 * @param name the page's name. 053 * @param title the page's title. 054 * @param configuration the configuration 055 */ 056 public FirstLevelPage(Page root, VirtualPageConfiguration configuration, FirstLevelPageFactory factory, String name, String title) 057 { 058 super(root, configuration, factory.getScheme(), factory); 059 060 _name = name; 061 _title = title; 062 } 063 064 @Override 065 public int getDepth() throws AmetysRepositoryException 066 { 067 return _root.getDepth() + 1; 068 } 069 070 @Override 071 public String getTitle() throws AmetysRepositoryException 072 { 073 return _title; 074 } 075 076 @Override 077 public String getLongTitle() throws AmetysRepositoryException 078 { 079 return _title; 080 } 081 082 @Override 083 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 084 { 085 if (!_hasSecondLevelPages()) 086 { 087 List<ProgramPage> pages = _getChildrenProgramPageFromCache() 088 .orElseGet(() -> _factory.getODFPageHandler().getProgramsWithRestrictions(_root, _name, null, null, null)) 089 .stream() 090 .map(this::_createProgramPage) 091 .collect(Collectors.toList()); 092 093 return new CollectionIterable<>(pages); 094 095 } 096 else 097 { 098 Map<String, Collection<Program>> secondLevelCache = Optional.ofNullable(_factory.getODFPageCache().getProgramCache(_root, true).get(_name)).orElseGet(HashMap::new); 099 Map<String, LevelValue> secondLevelValues = _factory.getODFPageHandler().getLevel2Values(_root); 100 101 List<Page> children = secondLevelCache.keySet().stream() 102 .map(secondLevelCode -> _findSecondLevelValueEntry(secondLevelValues, secondLevelCode).orElse(null)) 103 .filter(Objects::nonNull) 104 .map(entry -> _createSecondLevelPage(entry)) 105 .collect(Collectors.toList()); 106 107 return new CollectionIterable<>(children); 108 } 109 } 110 111 private boolean _hasSecondLevelPages() 112 { 113 String level2Metadata = _factory.getODFPageHandler().getLevel2Metadata(_root); 114 return StringUtils.isNotBlank(level2Metadata); 115 } 116 117 private Optional<AmetysObjectIterable<Program>> _getChildrenProgramPageFromCache() 118 { 119 return _factory.getODFPageCache().getPrograms(_root, _name, null, false); 120 } 121 122 private Optional<Entry<String, LevelValue>> _findSecondLevelValueEntry(String levelCode) 123 { 124 Map<String, LevelValue> secondLevelValues = _factory.getODFPageHandler().getLevel2Values(_root); 125 return _findSecondLevelValueEntry(secondLevelValues, levelCode); 126 } 127 128 private Optional<Entry<String, LevelValue>> _findSecondLevelValueEntry(Map<String, LevelValue> secondLevelValues, String levelCode) 129 { 130 return secondLevelValues.entrySet().stream() 131 // entry = (code, title) 132 .filter(entry -> entry.getKey().equals(levelCode)) 133 .findFirst(); 134 } 135 136 private SecondLevelPage _createSecondLevelPage(Map.Entry<String, LevelValue> secondLevelValueEntry) 137 { 138 String childName = secondLevelValueEntry.getKey(); 139 String title = secondLevelValueEntry.getValue().getValue(); 140 141 return _factory.getSecondLevelPageFactory().createSecondLevelPage(_root, _name, childName, title, this); 142 } 143 144 private ProgramPage _createProgramPage(Program program) 145 { 146 return _factory.getProgramPageFactory().createProgramPage(_root, program, null, null, this); 147 } 148 149 @Override 150 public String getPathInSitemap() throws AmetysRepositoryException 151 { 152 return _root.getPathInSitemap() + "/" + getName(); 153 } 154 155 @SuppressWarnings("unchecked") 156 @Override 157 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 158 { 159 if (path.isEmpty()) 160 { 161 throw new AmetysRepositoryException("path must be non empty"); 162 } 163 164 if (!_hasSecondLevelPages()) 165 { 166 return (A) _factory.getODFPageCache().getChildProgramPage(_root, this, _name, null, path); 167 } 168 else 169 { 170 int i = path.indexOf('/'); 171 172 String name = i == -1 ? path : path.substring(0, path.indexOf('/')); 173 174 int index = name.lastIndexOf("-"); 175 String level2Code = index != -1 ? _factory.getODFPageHandler().decodeLevelValue(name.substring(index + 1)) : _factory.getODFPageHandler().decodeLevelValue(name); 176 String queuePath = i == -1 ? null : path.substring(i + 1); 177 178 return (A) _findSecondLevelValueEntry(level2Code) 179 .map(this::_createSecondLevelPage) 180 .map(cp -> _factory.getODFPageHandler().addRedirectIfNeeded(cp, name)) 181 .map(cp -> _factory.getODFPageHandler().exploreQueuePath(cp, queuePath)) 182 .orElseThrow(() -> new UnknownAmetysObjectException("Unknown child page '" + path + "' for page " + _name)); 183 } 184 } 185 186 @SuppressWarnings("unchecked") 187 @Override 188 public AmetysObjectIterable<? extends Page> getChildren() throws AmetysRepositoryException 189 { 190 return getChildrenPages(); 191 } 192 193 @Override 194 public boolean hasChild(String childName) throws AmetysRepositoryException 195 { 196 // FIXME should use page cache, because can return true when page does 197 // not exist. Maybe done like this for performance reasons? 198 if (!_hasSecondLevelPages()) 199 { 200 return _factory.getODFPageCache().getProgramFromPageName(_root, _name, null, childName).isPresent(); 201 } 202 else 203 { 204 Map<String, LevelValue> secondLevelValues = _factory.getODFPageHandler().getLevel2Values(_root); 205 return secondLevelValues.containsKey(childName); 206 } 207 } 208 209 @Override 210 public String getId() throws AmetysRepositoryException 211 { 212 // E.g: odfLevel1://XA?rootId=... 213 return _factory.getODFPageHandler().buildLevel1Id(_root, _name); 214 } 215 216 @Override 217 public String getName() throws AmetysRepositoryException 218 { 219 // E.g: licence-lmd-XA 220 return NameHelper.filterName(_title) + "-" + _factory.getODFPageHandler().encodeLevelValue(_name); 221 } 222 223 @SuppressWarnings("unchecked") 224 @Override 225 public Page getParent() throws AmetysRepositoryException 226 { 227 return _root; 228 } 229 230 @Override 231 public String getParentPath() throws AmetysRepositoryException 232 { 233 return _root.getPath(); 234 } 235}