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