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.List; 020import java.util.Map; 021import java.util.Map.Entry; 022import java.util.Objects; 023import java.util.Optional; 024import java.util.stream.Collectors; 025 026import org.apache.commons.lang3.StringUtils; 027 028import org.ametys.odf.program.Program; 029import org.ametys.odf.tree.OdfClassificationHandler.LevelValue; 030import org.ametys.plugins.repository.AmetysObjectFactory; 031import org.ametys.plugins.repository.AmetysObjectIterable; 032import org.ametys.plugins.repository.AmetysRepositoryException; 033import org.ametys.plugins.repository.CollectionIterable; 034import org.ametys.plugins.repository.EmptyIterable; 035import org.ametys.plugins.repository.UnknownAmetysObjectException; 036import org.ametys.plugins.repository.jcr.JCRAmetysObject; 037import org.ametys.plugins.repository.virtual.VirtualAmetysObjectFactory; 038import org.ametys.web.repository.page.Page; 039import org.ametys.web.repository.page.virtual.VirtualPageConfiguration; 040 041/** 042 * {@link AmetysObjectFactory} handling {@link FirstLevelPage}. 043 * This factory is referenced by an ODF root page. 044 */ 045public class FirstLevelPageFactory extends AbstractOdfPageFactory implements VirtualAmetysObjectFactory<Page> 046{ 047 @Override 048 public String getScheme() 049 { 050 return "odfLevel1"; 051 } 052 053 @Override 054 public Page getAmetysObjectById(String id) throws AmetysRepositoryException 055 { 056 // E.g: odfLevel1://XA?rootId=xxx 057 String childName = _extractChildName(id); 058 Page rootPage = _extractRoot(id); 059 060 return _findFirstLevelValueEntry(rootPage, childName) 061 .map(entry -> _toFirstLevelPage(rootPage, entry)) 062 .orElseThrow(() -> 063 new UnknownAmetysObjectException("There's no virtual child page named " + childName + " for parent " + rootPage)); 064 } 065 066 private Page _extractRoot(String id) 067 { 068 String rootId = StringUtils.substringAfter(id, "?rootId="); 069 return _resolver.resolveById(rootId); 070 } 071 072 private String _extractChildName(String id) 073 { 074 String rawChildName = StringUtils.substringBetween(id, "://", "?"); 075 return _odfPageHandler.decodeLevelValue(rawChildName); 076 } 077 078 private Optional<Entry<String, LevelValue>> _findFirstLevelValueEntry(Page rootPage, String levelCode) 079 { 080 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 081 return _findFirstLevelValueEntry(firstLevelValues, levelCode); 082 } 083 084 private Optional<Entry<String, LevelValue>> _findFirstLevelValueEntry(Map<String, LevelValue> firstLevelValues, String levelCode) 085 { 086 return firstLevelValues.entrySet().stream() 087 // entry = (code, title) 088 .filter(entry -> entry.getKey().equals(levelCode)) 089 .findFirst(); 090 } 091 092 private FirstLevelPage _toFirstLevelPage(Page rootPage, Map.Entry<String, LevelValue> firstLevelValueEntry) throws AmetysRepositoryException 093 { 094 String childName = firstLevelValueEntry.getKey(); 095 String title = firstLevelValueEntry.getValue().getValue(); 096 097 VirtualPageConfiguration configuration = getConfiguration(); 098 099 return new FirstLevelPage(rootPage, configuration, this, childName, title); 100 } 101 102 private ProgramPage _toProgramPage(Program program, Page root) 103 { 104 ProgramPageFactory programPageFactory = getProgramPageFactory(); 105 return new ProgramPage(root, programPageFactory.getConfiguration(), programPageFactory, program, null, null, root); 106 } 107 108 @Override 109 public boolean hasAmetysObjectForId(String id) throws AmetysRepositoryException 110 { 111 // E.g: odfLevel1://XA?rootId=xxx 112 String childName = _extractChildName(id); 113 Page rootPage = _extractRoot(id); 114 115 return _hasFirstLevelValue(rootPage, childName); 116 } 117 118 @Override 119 public Page getChild(JCRAmetysObject parent, String childName) 120 { 121 if (!(parent instanceof Page rootPage)) 122 { 123 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 124 } 125 126 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 127 if (StringUtils.isBlank(level1Metadata)) 128 { 129 return _pageCache.getChildProgramPage(rootPage, rootPage, null, null, childName); 130 } 131 132 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 133 134 // E.g: licence-lmd-XA 135 int i = childName.lastIndexOf("-"); 136 String code = i != -1 ? _odfPageHandler.decodeLevelValue(childName.substring(i + 1)) : _odfPageHandler.decodeLevelValue(childName); // extract first level code 137 138 return Optional.of(code) 139 .map(firstLevelValues::get) 140 .map(LevelValue::getValue) 141 .map(title -> new FirstLevelPage(rootPage, getConfiguration(), this, code, title)) 142 .map(page -> _odfPageHandler.addRedirectIfNeeded(page, childName)) 143 .orElseThrow(() -> new UnknownAmetysObjectException("There's no virtual child page named " + childName + " for parent " + parent)); 144 } 145 146 @Override 147 public AmetysObjectIterable<Page> getChildren(JCRAmetysObject parent) 148 { 149 if (!(parent instanceof Page rootPage)) 150 { 151 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 152 } 153 154 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 155 if (StringUtils.isBlank(level1Metadata)) 156 { 157 List<Page> pages = _pageCache.getPrograms(rootPage, null, null, true) 158 .orElseGet(() -> new EmptyIterable<>()) 159 .stream() 160 .map(p -> _toProgramPage(p, rootPage)) 161 .collect(Collectors.toList()); 162 163 return new CollectionIterable<>(pages); 164 } 165 else 166 { 167 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 168 Map<String, Map<String, Collection<Program>>> firstLevelCache = _pageCache.getProgramCache(rootPage, true); 169 170 List<Page> children = firstLevelCache.keySet().stream() 171 .map(firstLevelCode -> _findFirstLevelValueEntry(firstLevelValues, firstLevelCode).orElse(null)) 172 .filter(Objects::nonNull) 173 .map(entry -> _toFirstLevelPage(rootPage, entry)) 174 .collect(Collectors.toList()); 175 176 return new CollectionIterable<>(children); 177 } 178 } 179 180 @Override 181 public boolean hasChild(JCRAmetysObject parent, String childName) 182 { 183 if (!(parent instanceof Page rootPage)) 184 { 185 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 186 } 187 188 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 189 if (StringUtils.isBlank(level1Metadata)) 190 { 191 Optional<Program> program = _pageCache.getProgramFromPageName(rootPage, null, null, childName); 192 return program.isPresent(); 193 } 194 else 195 { 196 return _hasFirstLevelValue(rootPage, childName); 197 } 198 } 199 200 private boolean _hasFirstLevelValue(Page rootPage, String level1Code) throws AmetysRepositoryException 201 { 202 // FIXME should use page cache, because can return true when page does 203 // not exist. Maybe done like this for performance reasons? 204 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 205 return firstLevelValues.containsKey(level1Code); 206 } 207}