1d8a7afe2SAdam Hornacek /* 2d8a7afe2SAdam Hornacek * CDDL HEADER START 3d8a7afe2SAdam Hornacek * 4d8a7afe2SAdam Hornacek * The contents of this file are subject to the terms of the 5d8a7afe2SAdam Hornacek * Common Development and Distribution License (the "License"). 6d8a7afe2SAdam Hornacek * You may not use this file except in compliance with the License. 7d8a7afe2SAdam Hornacek * 8d8a7afe2SAdam Hornacek * See LICENSE.txt included in this distribution for the specific 9d8a7afe2SAdam Hornacek * language governing permissions and limitations under the License. 10d8a7afe2SAdam Hornacek * 11d8a7afe2SAdam Hornacek * When distributing Covered Code, include this CDDL HEADER in each 12d8a7afe2SAdam Hornacek * file and include the License file at LICENSE.txt. 13d8a7afe2SAdam Hornacek * If applicable, add the following below this CDDL HEADER, with the 14d8a7afe2SAdam Hornacek * fields enclosed by brackets "[]" replaced with your own identifying 15d8a7afe2SAdam Hornacek * information: Portions Copyright [yyyy] [name of copyright owner] 16d8a7afe2SAdam Hornacek * 17d8a7afe2SAdam Hornacek * CDDL HEADER END 18d8a7afe2SAdam Hornacek */ 19d8a7afe2SAdam Hornacek 20d8a7afe2SAdam Hornacek /* 21*c6f0939bSAdam Hornacek * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. 22d8a7afe2SAdam Hornacek * Portions Copyright (c) 2018, Chris Fraire <cfraire@me.com>. 23d8a7afe2SAdam Hornacek * Portions Copyright (c) 2019, Krystof Tulinger <k.tulinger@seznam.cz>. 24d8a7afe2SAdam Hornacek */ 25d8a7afe2SAdam Hornacek package org.opengrok.web; 26d8a7afe2SAdam Hornacek 27d8a7afe2SAdam Hornacek import java.util.ArrayList; 28d8a7afe2SAdam Hornacek import java.util.Comparator; 29d8a7afe2SAdam Hornacek import java.util.List; 30d8a7afe2SAdam Hornacek import java.util.Locale; 31d8a7afe2SAdam Hornacek import java.util.Map; 32d8a7afe2SAdam Hornacek import java.util.Set; 33d8a7afe2SAdam Hornacek import java.util.TreeMap; 34d8a7afe2SAdam Hornacek import java.util.TreeSet; 35d8a7afe2SAdam Hornacek import java.util.stream.Collectors; 36d8a7afe2SAdam Hornacek import org.opengrok.indexer.configuration.Group; 37d8a7afe2SAdam Hornacek import org.opengrok.indexer.configuration.Project; 38d8a7afe2SAdam Hornacek import org.opengrok.indexer.history.RepositoryInfo; 39d8a7afe2SAdam Hornacek 40d8a7afe2SAdam Hornacek import static org.opengrok.web.PageConfig.OPEN_GROK_PROJECT; 41d8a7afe2SAdam Hornacek 42d8a7afe2SAdam Hornacek /** 43d8a7afe2SAdam Hornacek * Preprocessing of projects, repositories and groups for the UI. 44d8a7afe2SAdam Hornacek * 45d8a7afe2SAdam Hornacek * @author Krystof Tulinger 46d8a7afe2SAdam Hornacek */ 47d8a7afe2SAdam Hornacek public final class ProjectHelper { 48d8a7afe2SAdam Hornacek 49d8a7afe2SAdam Hornacek private static final String ATTR_NAME = "project_helper"; 50d8a7afe2SAdam Hornacek 51d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_GROUPS = "project_helper_groups"; 52d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_UNGROUPED_PROJECTS = "project_helper_ungrouped_projects"; 53d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_UNGROUPED_REPOSITORIES = "project_helper_ungrouped_repositories"; 54d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_GROUPED_PROJECT_GROUP = "project_helper_grouped_project_group_"; 55d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_GROUPED_REPOSITORIES = "project_helper_grouped_repositories"; 56d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_ALLOWED_SUBGROUP = "project_helper_allowed_subgroup"; 57d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_GROUPED_REPOSITORIES_GROUP = "project_helper_grouped_repositories_group_"; 58d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_GROUPED_PROJECTS = "project_helper_grouped_projects"; 59d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_SUBGROUPS_OF = "project_helper_subgroups_of_"; 60d8a7afe2SAdam Hornacek private static final String PROJECT_HELPER_FAVOURITE_GROUP = "project_helper_favourite_group"; 61d8a7afe2SAdam Hornacek 62d8a7afe2SAdam Hornacek private static final Comparator<RepositoryInfo> REPOSITORY_NAME_COMPARATOR = Comparator.comparing(RepositoryInfo::getDirectoryName); 63d8a7afe2SAdam Hornacek 64d8a7afe2SAdam Hornacek private PageConfig cfg; 65d8a7afe2SAdam Hornacek /** 66d8a7afe2SAdam Hornacek * Set of groups. 67d8a7afe2SAdam Hornacek */ 68d8a7afe2SAdam Hornacek private final Set<Group> groups; 69d8a7afe2SAdam Hornacek /** 70d8a7afe2SAdam Hornacek * Set of projects (not repositories) without group. 71d8a7afe2SAdam Hornacek */ 72d8a7afe2SAdam Hornacek private final Set<Project> ungroupedProjects; 73d8a7afe2SAdam Hornacek /** 74d8a7afe2SAdam Hornacek * Set of all repositories without group. 75d8a7afe2SAdam Hornacek */ 76d8a7afe2SAdam Hornacek private final Set<Project> ungroupedRepositories; 77d8a7afe2SAdam Hornacek /** 78d8a7afe2SAdam Hornacek * Set of all projects with group. 79d8a7afe2SAdam Hornacek */ 80d8a7afe2SAdam Hornacek private final Set<Project> allProjects = new TreeSet<>(); 81d8a7afe2SAdam Hornacek /** 82d8a7afe2SAdam Hornacek * Set of all repositories with group. 83d8a7afe2SAdam Hornacek */ 84d8a7afe2SAdam Hornacek private final Set<Project> allRepositories = new TreeSet<>(); 85d8a7afe2SAdam Hornacek ProjectHelper(PageConfig cfg)86d8a7afe2SAdam Hornacek private ProjectHelper(PageConfig cfg) { 87d8a7afe2SAdam Hornacek this.cfg = cfg; 88d8a7afe2SAdam Hornacek groups = new TreeSet<>(cfg.getEnv().getGroups()); 89d8a7afe2SAdam Hornacek ungroupedProjects = new TreeSet<>(); 90d8a7afe2SAdam Hornacek ungroupedRepositories = new TreeSet<>(); 91d8a7afe2SAdam Hornacek 92d8a7afe2SAdam Hornacek populateGroups(); 93d8a7afe2SAdam Hornacek } 94d8a7afe2SAdam Hornacek 95d8a7afe2SAdam Hornacek /** 96d8a7afe2SAdam Hornacek * Object of project helper should be ONLY obtained by calling 97d8a7afe2SAdam Hornacek * PageConfig#getProjectHelper. 98d8a7afe2SAdam Hornacek * 99d8a7afe2SAdam Hornacek * @param cfg current page config 100d8a7afe2SAdam Hornacek * @return instance of ProjectHelper 101d8a7afe2SAdam Hornacek * @see PageConfig#getProjectHelper() 102d8a7afe2SAdam Hornacek */ getInstance(PageConfig cfg)103d8a7afe2SAdam Hornacek public static ProjectHelper getInstance(PageConfig cfg) { 104d8a7afe2SAdam Hornacek ProjectHelper instance = (ProjectHelper) cfg.getRequestAttribute(ATTR_NAME); 105d8a7afe2SAdam Hornacek if (instance == null) { 106d8a7afe2SAdam Hornacek instance = new ProjectHelper(cfg); 107d8a7afe2SAdam Hornacek cfg.setRequestAttribute(ATTR_NAME, instance); 108d8a7afe2SAdam Hornacek } 109d8a7afe2SAdam Hornacek return instance; 110d8a7afe2SAdam Hornacek } 111d8a7afe2SAdam Hornacek 112d8a7afe2SAdam Hornacek /** 113d8a7afe2SAdam Hornacek * Get repository info list for particular project. A copy of the list is 114d8a7afe2SAdam Hornacek * returned always to allow concurrent modifications of the list in the 115d8a7afe2SAdam Hornacek * caller. The items in the list shall not be modified concurrently, though. 116d8a7afe2SAdam Hornacek * 117d8a7afe2SAdam Hornacek * @param p the project for which we find the repository info list 118d8a7afe2SAdam Hornacek * @return Copy of a list of repository info or empty list if no info is 119d8a7afe2SAdam Hornacek * found 120d8a7afe2SAdam Hornacek */ getRepositoryInfo(Project p)121d8a7afe2SAdam Hornacek public List<RepositoryInfo> getRepositoryInfo(Project p) { 122d8a7afe2SAdam Hornacek if (!cfg.isAllowed(p)) { 123d8a7afe2SAdam Hornacek return new ArrayList<>(); 124d8a7afe2SAdam Hornacek } 125d8a7afe2SAdam Hornacek Map<Project, List<RepositoryInfo>> map = cfg.getEnv().getProjectRepositoriesMap(); 126d8a7afe2SAdam Hornacek List<RepositoryInfo> info = map.get(p); 127d8a7afe2SAdam Hornacek return info == null ? new ArrayList<>() : new ArrayList<>(info); 128d8a7afe2SAdam Hornacek } 129d8a7afe2SAdam Hornacek 130d8a7afe2SAdam Hornacek /** 131d8a7afe2SAdam Hornacek * Get repository info list for particular project. A copy of the list is 132d8a7afe2SAdam Hornacek * returned always to allow concurrent modifications of the list in the 133d8a7afe2SAdam Hornacek * caller. The items in the list shall not be modified concurrently, though. 134d8a7afe2SAdam Hornacek * This list is sorted with respect {@link #REPOSITORY_NAME_COMPARATOR}. 135d8a7afe2SAdam Hornacek * 136d8a7afe2SAdam Hornacek * @param p the project for which we find the repository info list 137d8a7afe2SAdam Hornacek * @return Copy of a list of repository info or empty list if no info is 138d8a7afe2SAdam Hornacek * found 139d8a7afe2SAdam Hornacek */ getSortedRepositoryInfo(Project p)140d8a7afe2SAdam Hornacek public List<RepositoryInfo> getSortedRepositoryInfo(Project p) { 141d8a7afe2SAdam Hornacek return getRepositoryInfo(p) 142d8a7afe2SAdam Hornacek .stream() 143d8a7afe2SAdam Hornacek .sorted(REPOSITORY_NAME_COMPARATOR) 144d8a7afe2SAdam Hornacek .collect(Collectors.toList()); 145d8a7afe2SAdam Hornacek } 146d8a7afe2SAdam Hornacek 147d8a7afe2SAdam Hornacek /** 148d8a7afe2SAdam Hornacek * Generates ungrouped projects and repositories. 149d8a7afe2SAdam Hornacek */ populateGroups()150d8a7afe2SAdam Hornacek private void populateGroups() { 151d8a7afe2SAdam Hornacek groups.addAll(cfg.getEnv().getGroups()); 152d8a7afe2SAdam Hornacek for (Project project : cfg.getEnv().getProjectList()) { 153d8a7afe2SAdam Hornacek // filterProjects() only adds groups which match project's name. 154d8a7afe2SAdam Hornacek Set<Group> copy = Group.matching(project, groups); 155d8a7afe2SAdam Hornacek 156d8a7afe2SAdam Hornacek // If no group matches the project, add it to not-grouped projects. 157d8a7afe2SAdam Hornacek if (copy.isEmpty()) { 158d8a7afe2SAdam Hornacek if (cfg.getEnv().getProjectRepositoriesMap().get(project) == null) { 159d8a7afe2SAdam Hornacek ungroupedProjects.add(project); 160d8a7afe2SAdam Hornacek } else { 161d8a7afe2SAdam Hornacek ungroupedRepositories.add(project); 162d8a7afe2SAdam Hornacek } 163d8a7afe2SAdam Hornacek } 164d8a7afe2SAdam Hornacek } 165d8a7afe2SAdam Hornacek 166d8a7afe2SAdam Hornacek // populate all grouped 167d8a7afe2SAdam Hornacek for (Group g : getGroups()) { 168d8a7afe2SAdam Hornacek allProjects.addAll(g.getProjects()); 169d8a7afe2SAdam Hornacek allRepositories.addAll(g.getRepositories()); 170d8a7afe2SAdam Hornacek } 171d8a7afe2SAdam Hornacek } 172d8a7afe2SAdam Hornacek 173d8a7afe2SAdam Hornacek /** 174d8a7afe2SAdam Hornacek * Filters set of projects based on the authorizer options 175d8a7afe2SAdam Hornacek * and whether the project is indexed. 176d8a7afe2SAdam Hornacek * 177d8a7afe2SAdam Hornacek * @param p set of projects 178d8a7afe2SAdam Hornacek * @return filtered set of projects 179d8a7afe2SAdam Hornacek */ filterProjects(Set<Project> p)180d8a7afe2SAdam Hornacek private Set<Project> filterProjects(Set<Project> p) { 181d8a7afe2SAdam Hornacek Set<Project> repos = new TreeSet<>(p); 182*c6f0939bSAdam Hornacek repos.removeIf(t -> !cfg.isAllowed(t) || !t.isIndexed()); 183d8a7afe2SAdam Hornacek return repos; 184d8a7afe2SAdam Hornacek } 185d8a7afe2SAdam Hornacek 186d8a7afe2SAdam Hornacek /** 187d8a7afe2SAdam Hornacek * Filters set of groups based on the authorizer options. 188d8a7afe2SAdam Hornacek * 189d8a7afe2SAdam Hornacek * @param p set of groups 190d8a7afe2SAdam Hornacek * @return filtered set of groups 191d8a7afe2SAdam Hornacek */ filterGroups(Set<Group> p)192d8a7afe2SAdam Hornacek private Set<Group> filterGroups(Set<Group> p) { 193d8a7afe2SAdam Hornacek Set<Group> grps = new TreeSet<>(p); 194*c6f0939bSAdam Hornacek grps.removeIf(t -> !(cfg.isAllowed(t) || hasAllowedSubgroup(t))); 195d8a7afe2SAdam Hornacek return grps; 196d8a7afe2SAdam Hornacek } 197d8a7afe2SAdam Hornacek 198d8a7afe2SAdam Hornacek /** 199d8a7afe2SAdam Hornacek * Filters and saves the original set of projects into request's attribute. 200d8a7afe2SAdam Hornacek * 201d8a7afe2SAdam Hornacek * @param name attribute name 202d8a7afe2SAdam Hornacek * @param original original set 203d8a7afe2SAdam Hornacek * @return filtered set 204d8a7afe2SAdam Hornacek */ 205d8a7afe2SAdam Hornacek @SuppressWarnings(value = "unchecked") cacheProjects(String name, Set<Project> original)206d8a7afe2SAdam Hornacek private Set<Project> cacheProjects(String name, Set<Project> original) { 207d8a7afe2SAdam Hornacek Set<Project> p = (Set<Project>) cfg.getRequestAttribute(name); 208d8a7afe2SAdam Hornacek if (p == null) { 209d8a7afe2SAdam Hornacek p = filterProjects(original); 210d8a7afe2SAdam Hornacek cfg.setRequestAttribute(name, p); 211d8a7afe2SAdam Hornacek } 212d8a7afe2SAdam Hornacek return p; 213d8a7afe2SAdam Hornacek } 214d8a7afe2SAdam Hornacek 215d8a7afe2SAdam Hornacek /** 216d8a7afe2SAdam Hornacek * Filters and saves the original set of groups into request's attribute. 217d8a7afe2SAdam Hornacek * 218d8a7afe2SAdam Hornacek * @param name attribute name 219d8a7afe2SAdam Hornacek * @param original original set 220d8a7afe2SAdam Hornacek * @return filtered set 221d8a7afe2SAdam Hornacek */ 222d8a7afe2SAdam Hornacek @SuppressWarnings(value = "unchecked") cacheGroups(String name, Set<Group> original)223d8a7afe2SAdam Hornacek private Set<Group> cacheGroups(String name, Set<Group> original) { 224d8a7afe2SAdam Hornacek Set<Group> p = (Set<Group>) cfg.getRequestAttribute(name); 225d8a7afe2SAdam Hornacek if (p == null) { 226d8a7afe2SAdam Hornacek p = filterGroups(original); 227d8a7afe2SAdam Hornacek cfg.setRequestAttribute(name, p); 228d8a7afe2SAdam Hornacek } 229d8a7afe2SAdam Hornacek return p; 230d8a7afe2SAdam Hornacek } 231d8a7afe2SAdam Hornacek 232d8a7afe2SAdam Hornacek /** 233d8a7afe2SAdam Hornacek * @return filtered groups 234d8a7afe2SAdam Hornacek */ getGroups()235d8a7afe2SAdam Hornacek public Set<Group> getGroups() { 236d8a7afe2SAdam Hornacek return cacheGroups(PROJECT_HELPER_GROUPS, groups); 237d8a7afe2SAdam Hornacek } 238d8a7afe2SAdam Hornacek 239d8a7afe2SAdam Hornacek /** 240d8a7afe2SAdam Hornacek * @return filtered ungrouped projects 241d8a7afe2SAdam Hornacek */ getProjects()242d8a7afe2SAdam Hornacek public Set<Project> getProjects() { 243d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_UNGROUPED_PROJECTS, ungroupedProjects); 244d8a7afe2SAdam Hornacek } 245d8a7afe2SAdam Hornacek 246d8a7afe2SAdam Hornacek /** 247d8a7afe2SAdam Hornacek * @return filtered ungrouped repositories 248d8a7afe2SAdam Hornacek */ getRepositories()249d8a7afe2SAdam Hornacek public Set<Project> getRepositories() { 250d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_UNGROUPED_REPOSITORIES, ungroupedRepositories); 251d8a7afe2SAdam Hornacek } 252d8a7afe2SAdam Hornacek 253d8a7afe2SAdam Hornacek /** 254d8a7afe2SAdam Hornacek * @param g group 255d8a7afe2SAdam Hornacek * @return filtered group's projects 256d8a7afe2SAdam Hornacek */ getProjects(Group g)257d8a7afe2SAdam Hornacek public Set<Project> getProjects(Group g) { 258d8a7afe2SAdam Hornacek if (!cfg.isAllowed(g)) { 259d8a7afe2SAdam Hornacek return new TreeSet<>(); 260d8a7afe2SAdam Hornacek } 261d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_GROUPED_PROJECT_GROUP + 262d8a7afe2SAdam Hornacek g.getName().toLowerCase(Locale.ROOT), g.getProjects()); 263d8a7afe2SAdam Hornacek } 264d8a7afe2SAdam Hornacek 265d8a7afe2SAdam Hornacek /** 266d8a7afe2SAdam Hornacek * @param g group 267d8a7afe2SAdam Hornacek * @return filtered group's repositories 268d8a7afe2SAdam Hornacek */ getRepositories(Group g)269d8a7afe2SAdam Hornacek public Set<Project> getRepositories(Group g) { 270d8a7afe2SAdam Hornacek if (!cfg.isAllowed(g)) { 271d8a7afe2SAdam Hornacek return new TreeSet<>(); 272d8a7afe2SAdam Hornacek } 273d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_GROUPED_REPOSITORIES_GROUP + 274d8a7afe2SAdam Hornacek g.getName().toLowerCase(Locale.ROOT), g.getRepositories()); 275d8a7afe2SAdam Hornacek } 276d8a7afe2SAdam Hornacek 277d8a7afe2SAdam Hornacek /** 278d8a7afe2SAdam Hornacek * @return filtered grouped projects 279d8a7afe2SAdam Hornacek */ getGroupedProjects()280d8a7afe2SAdam Hornacek public Set<Project> getGroupedProjects() { 281d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_GROUPED_PROJECTS, allProjects); 282d8a7afe2SAdam Hornacek } 283d8a7afe2SAdam Hornacek 284d8a7afe2SAdam Hornacek /** 285d8a7afe2SAdam Hornacek * @return filtered grouped repositories 286d8a7afe2SAdam Hornacek */ getGroupedRepositories()287d8a7afe2SAdam Hornacek public Set<Project> getGroupedRepositories() { 288d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_GROUPED_REPOSITORIES, allRepositories); 289d8a7afe2SAdam Hornacek } 290d8a7afe2SAdam Hornacek 291d8a7afe2SAdam Hornacek /** 292d8a7afe2SAdam Hornacek * @see #getProjects() 293d8a7afe2SAdam Hornacek * @return filtered ungrouped projects 294d8a7afe2SAdam Hornacek */ getUngroupedProjects()295d8a7afe2SAdam Hornacek public Set<Project> getUngroupedProjects() { 296d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_UNGROUPED_PROJECTS, ungroupedProjects); 297d8a7afe2SAdam Hornacek } 298d8a7afe2SAdam Hornacek 299d8a7afe2SAdam Hornacek /** 300d8a7afe2SAdam Hornacek * @see #getRepositories() 301d8a7afe2SAdam Hornacek * @return filtered ungrouped projects 302d8a7afe2SAdam Hornacek */ getUngroupedRepositories()303d8a7afe2SAdam Hornacek public Set<Project> getUngroupedRepositories() { 304d8a7afe2SAdam Hornacek return cacheProjects(PROJECT_HELPER_UNGROUPED_REPOSITORIES, ungroupedRepositories); 305d8a7afe2SAdam Hornacek } 306d8a7afe2SAdam Hornacek 307d8a7afe2SAdam Hornacek /** 308d8a7afe2SAdam Hornacek * @return filtered projects and repositories 309d8a7afe2SAdam Hornacek */ getAllGrouped()310d8a7afe2SAdam Hornacek public Set<Project> getAllGrouped() { 311d8a7afe2SAdam Hornacek return mergeProjects(getGroupedProjects(), getGroupedRepositories()); 312d8a7afe2SAdam Hornacek } 313d8a7afe2SAdam Hornacek 314d8a7afe2SAdam Hornacek /** 315d8a7afe2SAdam Hornacek * @param g group 316d8a7afe2SAdam Hornacek * @return filtered set of all projects and repositories in group g 317d8a7afe2SAdam Hornacek */ getAllGrouped(Group g)318d8a7afe2SAdam Hornacek public Set<Project> getAllGrouped(Group g) { 319d8a7afe2SAdam Hornacek if (!cfg.isAllowed(g)) { 320d8a7afe2SAdam Hornacek return new TreeSet<>(); 321d8a7afe2SAdam Hornacek } 322d8a7afe2SAdam Hornacek return mergeProjects(filterProjects(g.getProjects()), filterProjects(g.getRepositories())); 323d8a7afe2SAdam Hornacek } 324d8a7afe2SAdam Hornacek 325d8a7afe2SAdam Hornacek /** 326d8a7afe2SAdam Hornacek * @return filtered set of all projects and repositories without group 327d8a7afe2SAdam Hornacek */ getAllUngrouped()328d8a7afe2SAdam Hornacek public Set<Project> getAllUngrouped() { 329d8a7afe2SAdam Hornacek return mergeProjects(getUngroupedProjects(), getUngroupedRepositories()); 330d8a7afe2SAdam Hornacek } 331d8a7afe2SAdam Hornacek 332d8a7afe2SAdam Hornacek /** 333d8a7afe2SAdam Hornacek * @return filtered set of all projects and repositories no matter if 334d8a7afe2SAdam Hornacek * grouped or ungrouped 335d8a7afe2SAdam Hornacek */ getAllProjects()336d8a7afe2SAdam Hornacek public Set<Project> getAllProjects() { 337d8a7afe2SAdam Hornacek return mergeProjects(getAllUngrouped(), getAllGrouped()); 338d8a7afe2SAdam Hornacek } 339d8a7afe2SAdam Hornacek 340d8a7afe2SAdam Hornacek /** 341d8a7afe2SAdam Hornacek * @param g group 342d8a7afe2SAdam Hornacek * @return filtered set of subgroups 343d8a7afe2SAdam Hornacek */ getSubgroups(Group g)344d8a7afe2SAdam Hornacek public Set<Group> getSubgroups(Group g) { 345d8a7afe2SAdam Hornacek if (!cfg.isAllowed(g)) { 346d8a7afe2SAdam Hornacek return new TreeSet<>(); 347d8a7afe2SAdam Hornacek } 348d8a7afe2SAdam Hornacek return cacheGroups(PROJECT_HELPER_SUBGROUPS_OF + 349d8a7afe2SAdam Hornacek g.getName().toLowerCase(Locale.ROOT), g.getSubgroups()); 350d8a7afe2SAdam Hornacek } 351d8a7afe2SAdam Hornacek 352d8a7afe2SAdam Hornacek /** 353d8a7afe2SAdam Hornacek * Checks if given group contains a subgroup which is allowed by the 354d8a7afe2SAdam Hornacek * AuthorizationFramework. 355d8a7afe2SAdam Hornacek * 356d8a7afe2SAdam Hornacek * This should be used for deciding if this group should be written in the 357d8a7afe2SAdam Hornacek * group hierarchy in the resulting html because it contains other allowed 358d8a7afe2SAdam Hornacek * groups. 359d8a7afe2SAdam Hornacek * 360d8a7afe2SAdam Hornacek * @param group group 361d8a7afe2SAdam Hornacek * @return true it it has an allowed subgroup 362d8a7afe2SAdam Hornacek */ 363d8a7afe2SAdam Hornacek @SuppressWarnings(value = "unchecked") hasAllowedSubgroup(Group group)364d8a7afe2SAdam Hornacek public boolean hasAllowedSubgroup(Group group) { 365d8a7afe2SAdam Hornacek Boolean val; 366d8a7afe2SAdam Hornacek Map<String, Boolean> p = (Map<String, Boolean>) cfg.getRequestAttribute(PROJECT_HELPER_ALLOWED_SUBGROUP); 367d8a7afe2SAdam Hornacek if (p == null) { 368*c6f0939bSAdam Hornacek p = new TreeMap<>(); 369d8a7afe2SAdam Hornacek cfg.setRequestAttribute(PROJECT_HELPER_ALLOWED_SUBGROUP, p); 370d8a7afe2SAdam Hornacek } 371d8a7afe2SAdam Hornacek val = p.get(group.getName()); 372d8a7afe2SAdam Hornacek if (val == null) { 373d8a7afe2SAdam Hornacek val = cfg.isAllowed(group); 374d8a7afe2SAdam Hornacek val = val && !filterGroups(group.getDescendants()).isEmpty(); 375d8a7afe2SAdam Hornacek p = (Map<String, Boolean>) cfg.getRequestAttribute(PROJECT_HELPER_ALLOWED_SUBGROUP); 376d8a7afe2SAdam Hornacek p.put(group.getName(), val); 377d8a7afe2SAdam Hornacek } 378d8a7afe2SAdam Hornacek cfg.setRequestAttribute(PROJECT_HELPER_ALLOWED_SUBGROUP, p); 379d8a7afe2SAdam Hornacek return val; 380d8a7afe2SAdam Hornacek } 381d8a7afe2SAdam Hornacek 382d8a7afe2SAdam Hornacek /** 383d8a7afe2SAdam Hornacek * Checks if given group contains a favourite project. 384d8a7afe2SAdam Hornacek * 385d8a7afe2SAdam Hornacek * Favourite project is a project which is contained in the OpenGrokProject 386d8a7afe2SAdam Hornacek * cookie, i. e. it has been searched or viewed by the user. 387d8a7afe2SAdam Hornacek * 388d8a7afe2SAdam Hornacek * This should by used to determine if this group should be displayed 389d8a7afe2SAdam Hornacek * expanded or rolled up. 390d8a7afe2SAdam Hornacek * 391d8a7afe2SAdam Hornacek * @param group group 392d8a7afe2SAdam Hornacek * @return true if it has favourite project 393d8a7afe2SAdam Hornacek */ 394d8a7afe2SAdam Hornacek @SuppressWarnings(value = "unchecked") hasFavourite(Group group)395d8a7afe2SAdam Hornacek public boolean hasFavourite(Group group) { 396d8a7afe2SAdam Hornacek Boolean val; 397d8a7afe2SAdam Hornacek Map<String, Boolean> p = (Map<String, Boolean>) cfg.getRequestAttribute(PROJECT_HELPER_FAVOURITE_GROUP); 398d8a7afe2SAdam Hornacek if (p == null) { 399*c6f0939bSAdam Hornacek p = new TreeMap<>(); 400d8a7afe2SAdam Hornacek cfg.setRequestAttribute(PROJECT_HELPER_FAVOURITE_GROUP, p); 401d8a7afe2SAdam Hornacek } 402d8a7afe2SAdam Hornacek val = p.get(group.getName()); 403d8a7afe2SAdam Hornacek if (val == null) { 404d8a7afe2SAdam Hornacek Set<Project> favourite = getAllGrouped(); 405*c6f0939bSAdam Hornacek favourite.removeIf(t -> { 406d8a7afe2SAdam Hornacek // project is favourite 407d8a7afe2SAdam Hornacek if (!isFavourite(t)) { 408d8a7afe2SAdam Hornacek return true; 409d8a7afe2SAdam Hornacek } 410d8a7afe2SAdam Hornacek // project is contained in group repositories 411d8a7afe2SAdam Hornacek if (getRepositories(group).contains(t)) { 412d8a7afe2SAdam Hornacek return false; 413d8a7afe2SAdam Hornacek } 414d8a7afe2SAdam Hornacek // project is contained in group projects 415d8a7afe2SAdam Hornacek if (getProjects(group).contains(t)) { 416d8a7afe2SAdam Hornacek return false; 417d8a7afe2SAdam Hornacek } 418d8a7afe2SAdam Hornacek // project is contained in subgroup's repositories and projects 419d8a7afe2SAdam Hornacek for (Group g : filterGroups(group.getDescendants())) { 420d8a7afe2SAdam Hornacek if (getProjects(g).contains(t)) { 421d8a7afe2SAdam Hornacek return false; 422d8a7afe2SAdam Hornacek } 423d8a7afe2SAdam Hornacek if (getRepositories(g).contains(t)) { 424d8a7afe2SAdam Hornacek return false; 425d8a7afe2SAdam Hornacek } 426d8a7afe2SAdam Hornacek } 427d8a7afe2SAdam Hornacek return true; 428d8a7afe2SAdam Hornacek }); 429d8a7afe2SAdam Hornacek val = !favourite.isEmpty(); 430d8a7afe2SAdam Hornacek p.put(group.getName(), val); 431d8a7afe2SAdam Hornacek } 432d8a7afe2SAdam Hornacek cfg.setRequestAttribute(PROJECT_HELPER_FAVOURITE_GROUP, p); 433d8a7afe2SAdam Hornacek return val; 434d8a7afe2SAdam Hornacek } 435d8a7afe2SAdam Hornacek 436d8a7afe2SAdam Hornacek /** 437d8a7afe2SAdam Hornacek * Checks if the project is a favourite project. 438d8a7afe2SAdam Hornacek * 439d8a7afe2SAdam Hornacek * @param project project 440d8a7afe2SAdam Hornacek * @return true if it is favourite 441d8a7afe2SAdam Hornacek */ isFavourite(Project project)442d8a7afe2SAdam Hornacek public boolean isFavourite(Project project) { 443d8a7afe2SAdam Hornacek return cfg.getCookieVals(OPEN_GROK_PROJECT).contains(project.getName()); 444d8a7afe2SAdam Hornacek } 445d8a7afe2SAdam Hornacek 446d8a7afe2SAdam Hornacek /** 447d8a7afe2SAdam Hornacek * Checks if there is a favourite project in ungrouped projects. 448d8a7afe2SAdam Hornacek * 449d8a7afe2SAdam Hornacek * This should by used to determine if this 'other' section should be 450d8a7afe2SAdam Hornacek * displayed expanded or rolled up. 451d8a7afe2SAdam Hornacek * 452d8a7afe2SAdam Hornacek * @return true if there is 453d8a7afe2SAdam Hornacek */ hasUngroupedFavourite()454d8a7afe2SAdam Hornacek public boolean hasUngroupedFavourite() { 455d8a7afe2SAdam Hornacek for (Project p : getAllUngrouped()) { 456d8a7afe2SAdam Hornacek if (isFavourite(p)) { 457d8a7afe2SAdam Hornacek return true; 458d8a7afe2SAdam Hornacek } 459d8a7afe2SAdam Hornacek } 460d8a7afe2SAdam Hornacek return false; 461d8a7afe2SAdam Hornacek } 462d8a7afe2SAdam Hornacek mergeProjects(Set<Project> p1, Set<Project> p2)463d8a7afe2SAdam Hornacek private static Set<Project> mergeProjects(Set<Project> p1, Set<Project> p2) { 464*c6f0939bSAdam Hornacek Set<Project> set = new TreeSet<>(); 465d8a7afe2SAdam Hornacek set.addAll(p1); 466d8a7afe2SAdam Hornacek set.addAll(p2); 467d8a7afe2SAdam Hornacek return set; 468d8a7afe2SAdam Hornacek } 469d8a7afe2SAdam Hornacek cleanup(PageConfig cfg)470d8a7afe2SAdam Hornacek public static void cleanup(PageConfig cfg) { 471d8a7afe2SAdam Hornacek if (cfg != null) { 472d8a7afe2SAdam Hornacek ProjectHelper helper = (ProjectHelper) cfg.getRequestAttribute(ATTR_NAME); 473d8a7afe2SAdam Hornacek if (helper == null) { 474d8a7afe2SAdam Hornacek return; 475d8a7afe2SAdam Hornacek } 476d8a7afe2SAdam Hornacek cfg.removeAttribute(ATTR_NAME); 477d8a7afe2SAdam Hornacek helper.cfg = null; 478d8a7afe2SAdam Hornacek } 479d8a7afe2SAdam Hornacek } 480d8a7afe2SAdam Hornacek } 481