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