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