001/* 002 * Copyright 2016 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.core.ui; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * Dependencies manager, able to compute the full chain of dependencies. 031 */ 032public class ClientSideElementDependenciesManager 033{ 034 /** Logger */ 035 protected Logger _logger = LoggerFactory.getLogger(ClientSideElementDependenciesManager.class); 036 037 private Map<String, List<String>> _dependencies; 038 private ServiceManager _manager; 039 040 /** 041 * Default constructor for the dependencies manager. 042 * @param manager The service manager, used to resolve dependencies. 043 */ 044 public ClientSideElementDependenciesManager(ServiceManager manager) 045 { 046 _dependencies = new HashMap<>(); 047 _manager = manager; 048 } 049 050 /** 051 * Register a new dependency 052 * @param extensionPoint The dependency extension point 053 * @param extensionId The dependency extension 054 */ 055 public void register(String extensionPoint, String extensionId) 056 { 057 if (!_dependencies.containsKey(extensionPoint)) 058 { 059 _dependencies.put(extensionPoint, new ArrayList<>()); 060 } 061 062 List<String> extensions = _dependencies.get(extensionPoint); 063 064 if (!extensions.contains(extensionId)) 065 { 066 extensions.add(extensionId); 067 } 068 } 069 070 /** 071 * Register a new dependency to a client side element 072 * @param element The client side element 073 */ 074 public void register(ClientSideElement element) 075 { 076 Map<String, List<String>> dependencies = element.getDependencies(); 077 078 for (Entry<String, List<String>> entry : dependencies.entrySet()) 079 { 080 String extensionPoint = entry.getKey(); 081 082 for (String extensionId : entry.getValue()) 083 { 084 if (_logger.isDebugEnabled()) 085 { 086 _logger.debug("Register dependency : The extension '" + element.getId() + "' depends on '" + extensionId + "' of extension point '" + extensionPoint + "')"); 087 } 088 089 register(extensionPoint, extensionId); 090 } 091 } 092 } 093 094 /** 095 * Compute the chain of dependencies 096 * @return The list of dynamic dependencies calculated from the registered dependencies. 097 * @throws ServiceException If an error occurs with the list of ExtensionPoints 098 */ 099 public Map<String, List<ClientSideElement>> computeDependencies() throws ServiceException 100 { 101 Map<String, List<ClientSideElement>> computedDependencies = new HashMap<>(); 102 computeDependencies(computedDependencies, _dependencies, new ArrayList<>()); 103 return computedDependencies; 104 } 105 106 /** 107 * Recursively Compute the chain of dependency. 108 * @param computedDependencies The list of dependencies that have already been computed from the chain. This map is filled with the full dependencies chain. 109 * @param dependenciesToProcess The list of extensions to parse, mapped by extension points, that can have additional dependencies. 110 * @param knownElements The list of elements that were already handled (to avoid infinite loop) 111 * @throws ServiceException If an error occurs 112 */ 113 private void computeDependencies(Map<String, List<ClientSideElement>> computedDependencies, Map<String, List<String>> dependenciesToProcess, List<ClientSideElement> knownElements) throws ServiceException 114 { 115 for (Entry<String, List<String>> dependencyToProcess : dependenciesToProcess.entrySet()) 116 { 117 String extensionPointId = dependencyToProcess.getKey(); 118 @SuppressWarnings("unchecked") 119 AbstractClientSideExtensionPoint<ClientSideElement> extensionPoint = (AbstractClientSideExtensionPoint<ClientSideElement>) _manager.lookup(extensionPointId); 120 121 if (!computedDependencies.containsKey(extensionPointId)) 122 { 123 computedDependencies.put(extensionPointId, new ArrayList<>()); 124 } 125 126 for (String extensionIdToProcess : dependencyToProcess.getValue()) 127 { 128 for (ClientSideElement extension : extensionPoint.findDependency(extensionIdToProcess)) 129 { 130 if (extension != null && !knownElements.contains(extension)) 131 { 132 // only process extension once 133 knownElements.add(extension); 134 135 Map<String, List<String>> dependencies = extension.getDependencies(); 136 137 if (_logger.isDebugEnabled()) 138 { 139 _logger.debug("Computing dependencies : For extension point '" + extensionPointId + "', the extension : '" + extensionIdToProcess + "' requires the following dependencies : " + dependencies.toString()); 140 } 141 142 // Fill the computedDependencies list recursively, with the dependency's own dependencies. 143 computeDependencies(computedDependencies, dependencies, knownElements); 144 145 computedDependencies.get(extensionPointId).add(extension); 146 } 147 } 148 } 149 } 150 } 151}