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