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