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