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 String title = firstLevelValues.get(code).getValue(); 136 if (title != null) 137 { 138 return new FirstLevelPage(this, rootPage, code, title); 139 } 140 } 141 142 throw new UnknownAmetysObjectException("There's no virtual child page named " + childName + " for parent " + parent); 143 } 144 145 @Override 146 public AmetysObjectIterable<Page> getChildren(JCRAmetysObject parent) 147 { 148 if (!(parent instanceof Page)) 149 { 150 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 151 } 152 153 Page rootPage = (Page) parent; 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)) 184 { 185 throw new IllegalArgumentException("The holder of the ODF virtual pages should be a page."); 186 } 187 188 Page rootPage = (Page) parent; 189 String level1Metadata = _odfPageHandler.getLevel1Metadata(rootPage); 190 if (StringUtils.isBlank(level1Metadata)) 191 { 192 Optional<Program> program = _pageCache.getProgramFromPageName(rootPage, null, null, childName); 193 return program.isPresent(); 194 } 195 else 196 { 197 return _hasFirstLevelValue(rootPage, childName); 198 } 199 } 200 201 private boolean _hasFirstLevelValue(Page rootPage, String level1Code) throws AmetysRepositoryException 202 { 203 // FIXME should use page cache, because can return true when page does 204 // not exist. Maybe done like this for performance reasons? 205 Map<String, LevelValue> firstLevelValues = _odfPageHandler.getLevel1Values(rootPage); 206 return firstLevelValues.containsKey(level1Code); 207 } 208}