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)) 117 { 118 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 119 } 120 121 Page rootPage = (Page) parent; 122 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 123 if (StringUtils.isBlank(level1Metadata)) 124 { 125 return _pageCache.getChildProgramPage(rootPage, rootPage, null, null, childName); 126 } 127 else 128 { 129 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 130 131 // E.g: licence-lmd-XA 132 int i = childName.lastIndexOf("-"); 133 String code = i != -1 ? _odfPageHandler.decodeLevelValue(childName.substring(i + 1)) : _odfPageHandler.decodeLevelValue(childName); // extract first level code 134 135 Optional<FirstLevelPage> flp = Optional.of(code) 136 .map(firstLevelValues::get) 137 .map(LevelValue::getValue) 138 .map(title -> new FirstLevelPage(this, rootPage, code, title)); 139 140 if (flp.isPresent()) 141 { 142 return flp.get(); 143 } 144 } 145 146 throw new UnknownAmetysObjectException("There's no virtual child page named " + childName + " for parent " + parent); 147 } 148 149 @Override 150 public AmetysObjectIterable<Page> getChildren(JCRAmetysObject parent) 151 { 152 if (!(parent instanceof Page)) 153 { 154 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 155 } 156 157 Page rootPage = (Page) parent; 158 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 159 if (StringUtils.isBlank(level1Metadata)) 160 { 161 List<Page> pages = _pageCache.getPrograms(rootPage, null, null, true) 162 .orElseGet(() -> new EmptyIterable<>()) 163 .stream() 164 .map(p -> _toProgramPage(p, rootPage)) 165 .collect(Collectors.toList()); 166 167 return new CollectionIterable<>(pages); 168 } 169 else 170 { 171 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 172 Map<String, Map<String, Collection<Program>>> firstLevelCache = _pageCache.getProgramCache(rootPage, true); 173 174 List<Page> children = firstLevelCache.keySet().stream() 175 .map(firstLevelCode -> _findFirstLevelValueEntry(firstLevelValues, firstLevelCode).orElse(null)) 176 .filter(Objects::nonNull) 177 .map(entry -> _toFirstLevelPage(rootPage, entry)) 178 .collect(Collectors.toList()); 179 180 return new CollectionIterable<>(children); 181 } 182 } 183 184 @Override 185 public boolean hasChild(JCRAmetysObject parent, String childName) 186 { 187 if (!(parent instanceof Page)) 188 { 189 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 190 } 191 192 Page rootPage = (Page) parent; 193 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 194 if (StringUtils.isBlank(level1Metadata)) 195 { 196 Optional<Program> program = _pageCache.getProgramFromPageName(rootPage, null, null, childName); 197 return program.isPresent(); 198 } 199 else 200 { 201 return _hasFirstLevelValue(rootPage, childName); 202 } 203 } 204 205 private boolean _hasFirstLevelValue(Page rootPage, String level1Code) throws AmetysRepositoryException 206 { 207 // FIXME should use page cache, because can return true when page does 208 // not exist. Maybe done like this for performance reasons? 209 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 210 return firstLevelValues.containsKey(level1Code); 211 } 212}