xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Group.java (revision c6f0939b1c668e9f8e1e276424439c3106b3a029)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2018, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.configuration;
25 
26 import java.util.Locale;
27 import java.util.Set;
28 import java.util.TreeSet;
29 import java.util.regex.Pattern;
30 import java.util.regex.PatternSyntaxException;
31 import org.opengrok.indexer.util.ClassUtil;
32 
33 /**
34  * Placeholder for the information about subgroups of projects and repositories.
35  *
36  * Supports natural ordering based on case insensitive group names.
37  *
38  * @author Krystof Tulinger
39  * @version $Revision$
40  */
41 public class Group implements Comparable<Group>, Nameable {
42 
43     static {
44         ClassUtil.remarkTransientFields(Group.class);
45     }
46 
47     private String name;
48     /**
49      * Group regexp pattern.
50      *
51      * No project matches the empty pattern of "" however this group can still
52      * be used as a superior group for other groups (without duplicating the
53      * projects).
54      */
55     private String pattern = "";
56     /**
57      * Compiled group pattern.
58      *
59      * We set up the empty compiled pattern by default to "()" to reduce code
60      * complexity when performing a match for a group without a pattern.
61      *
62      * This pattern is updated whenever the string pattern {@link #pattern} is
63      * updated.
64      *
65      * @see #setPattern(String)
66      */
67     private Pattern compiledPattern = Pattern.compile("()");
68     private Group parent;
69     private Set<Group> subgroups = new TreeSet<>();
70 
71     private transient int flag;
72     private transient Set<Group> descendants = new TreeSet<>();
73     private transient Set<Project> projects = new TreeSet<>();
74     private transient Set<Project> repositories = new TreeSet<>();
75     private transient Set<Group> parents;
76 
77     /**
78      * No-arg constructor is needed for deserialization.
79      */
Group()80     public Group() {
81     }
82 
Group(String name)83     public Group(String name) {
84         this(name, "");
85     }
86 
Group(String name, String pattern)87     public Group(String name, String pattern) {
88         this.name = name;
89         this.setPattern(pattern);
90     }
91 
getProjects()92     public Set<Project> getProjects() {
93         return projects;
94     }
95 
addProject(Project p)96     public void addProject(Project p) {
97         this.projects.add(p);
98     }
99 
addRepository(Project p)100     public void addRepository(Project p) {
101         this.repositories.add(p);
102     }
103 
getDescendants()104     public Set<Group> getDescendants() {
105         return descendants;
106     }
107 
setDescendants(Set<Group> descendants)108     public void setDescendants(Set<Group> descendants) {
109         this.descendants = descendants;
110     }
111 
addDescendant(Group g)112     public void addDescendant(Group g) {
113         this.descendants.add(g);
114     }
115 
removeDescendant(Group g)116     public void removeDescendant(Group g) {
117         this.descendants.remove(g);
118     }
119 
getRepositories()120     public Set<Project> getRepositories() {
121         return repositories;
122     }
123 
setSubgroups(Set<Group> subgroups)124     public void setSubgroups(Set<Group> subgroups) {
125         this.subgroups = subgroups;
126     }
127 
setProjects(Set<Project> projects)128     public void setProjects(Set<Project> projects) {
129         this.projects = projects;
130     }
131 
setRepositories(Set<Project> repositories)132     public void setRepositories(Set<Project> repositories) {
133         this.repositories = repositories;
134     }
135 
getSubgroups()136     public Set<Group> getSubgroups() {
137         return subgroups;
138     }
139 
addGroup(Group g)140     public void addGroup(Group g) {
141         g.setParent(this);
142         subgroups.add(g);
143         descendants.add(g);
144     }
145 
getParents()146     public Set<Group> getParents() {
147         if (parents == null) {
148             parents = new TreeSet<>();
149             Group tmp = parent;
150             while (tmp != null) {
151                 parents.add(tmp);
152                 tmp = tmp.getParent();
153             }
154         }
155         return parents;
156     }
157 
158     /**
159      * Collect all related groups to this group. A related group is
160      * <ul>
161      * <li>any ancestor</li>
162      * <li>any subgroup</li>
163      * </ul>
164      *
165      * @return all collected related groups to this group
166      */
getRelatedGroups()167     public Set<Group> getRelatedGroups() {
168         Set<Group> groupsTmp = new TreeSet<>();
169         groupsTmp.addAll(getDescendants());
170         groupsTmp.addAll(getParents());
171         return groupsTmp;
172     }
173 
174     /**
175      * Collect all group's projects and repositories included in this group and
176      * in any subgroup.
177      *
178      * @return all collected projects and repositories
179      */
getAllProjects()180     public Set<Project> getAllProjects() {
181         Set<Project> projectsTmp = new TreeSet<>();
182         projectsTmp.addAll(getRepositories());
183         projectsTmp.addAll(getProjects());
184         for (Group grp : getDescendants()) {
185             projectsTmp.addAll(grp.getAllProjects());
186         }
187         return projectsTmp;
188     }
189 
getParent()190     public Group getParent() {
191         return parent;
192     }
193 
setParent(Group parent)194     public void setParent(Group parent) {
195         this.parent = parent;
196     }
197 
198     @Override
getName()199     public String getName() {
200         return name;
201     }
202 
203     @Override
setName(String name)204     public void setName(String name) {
205         this.name = name;
206     }
207 
getPattern()208     public String getPattern() {
209         return pattern;
210     }
211 
212     /**
213      * Set the group pattern.
214      *
215      * @param pattern the regexp pattern for this group
216      * @throws PatternSyntaxException when the pattern is invalid
217      */
setPattern(String pattern)218     public final void setPattern(String pattern) throws PatternSyntaxException {
219         this.compiledPattern = Pattern.compile("(" + pattern + ")");
220         this.pattern = pattern;
221     }
222 
getFlag()223     public int getFlag() {
224         return flag;
225     }
226 
setFlag(int flag)227     public void setFlag(int flag) {
228         this.flag = flag;
229     }
230 
231     /**
232      * Test group for a match.
233      *
234      * @param p project
235      * @return true if project's description matches the group pattern
236      */
match(Project p)237     public boolean match(Project p) {
238         return compiledPattern.matcher(p.getName()).matches();
239     }
240 
241     @Override
compareTo(Group o)242     public int compareTo(Group o) {
243         return getName().toUpperCase(Locale.ROOT).compareTo(
244                 o.getName().toUpperCase(Locale.ROOT));
245     }
246 
247     @Override
hashCode()248     public int hashCode() {
249         int hash = 3;
250         hash = 41 * hash + (this.name == null ? 0 :
251                 this.name.toUpperCase(Locale.ROOT).hashCode());
252         return hash;
253     }
254 
255     @Override
equals(Object obj)256     public boolean equals(Object obj) {
257         if (this == obj) {
258             return true;
259         }
260         if (obj == null) {
261             return false;
262         }
263         if (getClass() != obj.getClass()) {
264             return false;
265         }
266         final Group other = (Group) obj;
267 
268         int numNull = (name == null ? 1 : 0) + (other.name == null ? 1 : 0);
269         switch (numNull) {
270             case 0:
271                 return name.toUpperCase(Locale.ROOT).equals(
272                         other.name.toUpperCase(Locale.ROOT));
273             case 1:
274                 return false;
275             default:
276                 return true;
277         }
278     }
279 
280     /**
281      * Returns group object by its name.
282      *
283      * @param name name of a group
284      * @return group that fits the name
285      */
getByName(String name)286     public static Group getByName(String name) {
287         Group ret = null;
288         RuntimeEnvironment env = RuntimeEnvironment.getInstance();
289         if (env.hasGroups()) {
290             for (Group grp : env.getGroups()) {
291                 if (name.equals(grp.getName())) {
292                     ret = grp;
293                 }
294             }
295         }
296         return ret;
297     }
298 
299     /**
300      * Reduce the group set to only those which match the given project based on
301      * the project's description.
302      *
303      * @param project the project
304      * @param groups set of groups
305      * @return set of groups matching the project
306      */
matching(Project project, Set<Group> groups)307     public static Set<Group> matching(Project project, Set<Group> groups) {
308         Set<Group> copy = new TreeSet<>(groups);
309         copy.removeIf(g -> !g.match(project));
310         return copy;
311     }
312 }
313