xref: /OpenGrok/opengrok-web/src/main/java/org/opengrok/web/ProjectHelper.java (revision c6f0939b1c668e9f8e1e276424439c3106b3a029)
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