1b5840353SAdam Hornáček /* 2b5840353SAdam Hornáček * CDDL HEADER START 3b5840353SAdam Hornáček * 4b5840353SAdam Hornáček * The contents of this file are subject to the terms of the 5b5840353SAdam Hornáček * Common Development and Distribution License (the "License"). 6b5840353SAdam Hornáček * You may not use this file except in compliance with the License. 7b5840353SAdam Hornáček * 8b5840353SAdam Hornáček * See LICENSE.txt included in this distribution for the specific 9b5840353SAdam Hornáček * language governing permissions and limitations under the License. 10b5840353SAdam Hornáček * 11b5840353SAdam Hornáček * When distributing Covered Code, include this CDDL HEADER in each 12b5840353SAdam Hornáček * file and include the License file at LICENSE.txt. 13b5840353SAdam Hornáček * If applicable, add the following below this CDDL HEADER, with the 14b5840353SAdam Hornáček * fields enclosed by brackets "[]" replaced with your own identifying 15b5840353SAdam Hornáček * information: Portions Copyright [yyyy] [name of copyright owner] 16b5840353SAdam Hornáček * 17b5840353SAdam Hornáček * CDDL HEADER END 18b5840353SAdam Hornáček */ 19b5840353SAdam Hornáček 20b5840353SAdam Hornáček /* 217a438acaSVladimir Kotal * Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved. 2252dccac1SChris Fraire * Portions Copyright (c) 2018, Chris Fraire <cfraire@me.com>. 23b5840353SAdam Hornáček */ 249805b761SAdam Hornáček package org.opengrok.indexer.configuration; 25b5840353SAdam Hornáček 26b5840353SAdam Hornáček import java.io.File; 27b5840353SAdam Hornáček import java.io.FileNotFoundException; 28b5840353SAdam Hornáček import java.io.IOException; 29b5840353SAdam Hornáček import java.io.Serializable; 30b5840353SAdam Hornáček import java.util.Locale; 31b5840353SAdam Hornáček import java.util.Set; 32b5840353SAdam Hornáček import java.util.TreeSet; 33b5840353SAdam Hornáček import java.util.logging.Level; 34b5840353SAdam Hornáček import java.util.logging.Logger; 3564819763SVladimir Kotal import java.util.regex.PatternSyntaxException; 3664819763SVladimir Kotal 37a6a884eaSVladimir Kotal import org.jetbrains.annotations.VisibleForTesting; 389805b761SAdam Hornáček import org.opengrok.indexer.logger.LoggerFactory; 399805b761SAdam Hornáček import org.opengrok.indexer.util.ClassUtil; 409805b761SAdam Hornáček import org.opengrok.indexer.util.ForbiddenSymlinkException; 41807ead8fSLubos Kosco import org.opengrok.indexer.web.Util; 42b5840353SAdam Hornáček 4364819763SVladimir Kotal import static org.opengrok.indexer.configuration.PatternUtil.compilePattern; 4464819763SVladimir Kotal 45b5840353SAdam Hornáček /** 46ff44f24aSAdam Hornáček * Placeholder for the information that builds up a project. 47b5840353SAdam Hornáček */ 48b5840353SAdam Hornáček public class Project implements Comparable<Project>, Nameable, Serializable { 49b5840353SAdam Hornáček 50b5840353SAdam Hornáček private static final long serialVersionUID = 1L; 51b5840353SAdam Hornáček 52b5840353SAdam Hornáček private static final Logger LOGGER = LoggerFactory.getLogger(Project.class); 53b5840353SAdam Hornáček 54b5840353SAdam Hornáček static { 55b5840353SAdam Hornáček ClassUtil.remarkTransientFields(Project.class); 56b5840353SAdam Hornáček } 57b5840353SAdam Hornáček 58bd3709daSVladimir Kotal /** 59bd3709daSVladimir Kotal * Path relative to source root. Uses the '/' separator on all platforms. 60bd3709daSVladimir Kotal */ 61b5840353SAdam Hornáček private String path; 62b5840353SAdam Hornáček 63b5840353SAdam Hornáček /** 64b5840353SAdam Hornáček * This variable is very important, since it's used as the project 65b5840353SAdam Hornáček * identifier all over xrefs and webapp. 66b5840353SAdam Hornáček */ 67b5840353SAdam Hornáček private String name; 68b5840353SAdam Hornáček 69b5840353SAdam Hornáček /** 70b5840353SAdam Hornáček * Size of tabs in this project. Used for displaying the xrefs correctly in 71b5840353SAdam Hornáček * projects with non-standard tab size. 72b5840353SAdam Hornáček */ 73b5840353SAdam Hornáček private int tabSize; 74b5840353SAdam Hornáček 75b5840353SAdam Hornáček /** 76b5840353SAdam Hornáček * A flag if the navigate window should be opened by default when browsing 77b5840353SAdam Hornáček * the source code of this project. 78b5840353SAdam Hornáček */ 79326dace0SVladimir Kotal private Boolean navigateWindowEnabled = null; 80b5840353SAdam Hornáček 81b5840353SAdam Hornáček /** 82b5840353SAdam Hornáček * This flag sets per-project handling of renamed files. 83b5840353SAdam Hornáček */ 84b5840353SAdam Hornáček private Boolean handleRenamedFiles = null; 85b5840353SAdam Hornáček 86b5840353SAdam Hornáček /** 87b5840353SAdam Hornáček * This flag enables/disables per-project history cache. 88b5840353SAdam Hornáček */ 89b5840353SAdam Hornáček private Boolean historyEnabled = null; 90b5840353SAdam Hornáček 91b5840353SAdam Hornáček /** 928e5e1036SVladimir Kotal * This flag enables/disables per project merge commits. 938e5e1036SVladimir Kotal */ 948e5e1036SVladimir Kotal private Boolean mergeCommitsEnabled = null; 958e5e1036SVladimir Kotal 968e5e1036SVladimir Kotal /** 97b5840353SAdam Hornáček * This marks the project as (not)ready before initial index is done. this 98b5840353SAdam Hornáček * is to avoid all/multi-project searches referencing this project from 99b5840353SAdam Hornáček * failing. 100b5840353SAdam Hornáček */ 101b5840353SAdam Hornáček private boolean indexed = false; 102b5840353SAdam Hornáček 103b5840353SAdam Hornáček /** 104f4571972SVladimir Kotal * This flag sets per-project reindex based on traversing SCM history. 1057a438acaSVladimir Kotal */ 106f4571972SVladimir Kotal private Boolean historyBasedReindex = null; 1077a438acaSVladimir Kotal 1087a438acaSVladimir Kotal /** 109b5840353SAdam Hornáček * Set of groups which match this project. 110b5840353SAdam Hornáček */ 111b5840353SAdam Hornáček private transient Set<Group> groups = new TreeSet<>(); 112b5840353SAdam Hornáček 11364819763SVladimir Kotal /** 11464819763SVladimir Kotal * These properties override global settings, if set. 11564819763SVladimir Kotal */ 11664819763SVladimir Kotal private String bugPage; 11764819763SVladimir Kotal private String bugPattern; 11864819763SVladimir Kotal private String reviewPage; 11964819763SVladimir Kotal private String reviewPattern; 12064819763SVladimir Kotal 121b5840353SAdam Hornáček // This empty constructor is needed for serialization. Project()122b5840353SAdam Hornáček public Project() { 123b5840353SAdam Hornáček } 124b5840353SAdam Hornáček 125b5840353SAdam Hornáček /** 126b5840353SAdam Hornáček * Create a project with given name. 127b5840353SAdam Hornáček * 128b5840353SAdam Hornáček * @param name the name of the project 129b5840353SAdam Hornáček */ Project(String name)130b5840353SAdam Hornáček public Project(String name) { 131b5840353SAdam Hornáček this.name = name; 132b5840353SAdam Hornáček } 133b5840353SAdam Hornáček 134b5840353SAdam Hornáček /** 1352ffbb0cfSVladimir Kotal * Create a project with given name and path and default configuration 1362ffbb0cfSVladimir Kotal * values. 137b5840353SAdam Hornáček * 138b5840353SAdam Hornáček * @param name the name of the project 139b5840353SAdam Hornáček * @param path the path of the project relative to the source root 140b5840353SAdam Hornáček */ Project(String name, String path)141b5840353SAdam Hornáček public Project(String name, String path) { 142b5840353SAdam Hornáček this.name = name; 143807ead8fSLubos Kosco this.path = Util.fixPathIfWindows(path); 1442ffbb0cfSVladimir Kotal completeWithDefaults(); 145b5840353SAdam Hornáček } 146b5840353SAdam Hornáček 147b5840353SAdam Hornáček /** 148ff44f24aSAdam Hornáček * Get a textual name of this project. 149b5840353SAdam Hornáček * 150b5840353SAdam Hornáček * @return a textual name of the project 151b5840353SAdam Hornáček */ 152b5840353SAdam Hornáček @Override getName()153b5840353SAdam Hornáček public String getName() { 154b5840353SAdam Hornáček return name; 155b5840353SAdam Hornáček } 156b5840353SAdam Hornáček 157b5840353SAdam Hornáček /** 158ff44f24aSAdam Hornáček * Get the path (relative from source root) where this project is located. 159b5840353SAdam Hornáček * 160b5840353SAdam Hornáček * @return the relative path 161b5840353SAdam Hornáček */ getPath()162b5840353SAdam Hornáček public String getPath() { 163b5840353SAdam Hornáček return path; 164b5840353SAdam Hornáček } 165b5840353SAdam Hornáček isIndexed()166b5840353SAdam Hornáček public boolean isIndexed() { 167b5840353SAdam Hornáček return indexed; 168b5840353SAdam Hornáček } 169b5840353SAdam Hornáček 170b5840353SAdam Hornáček /** 171ff44f24aSAdam Hornáček * Get the project id. 172b5840353SAdam Hornáček * 173b5840353SAdam Hornáček * @return the id of the project 174b5840353SAdam Hornáček */ getId()175b5840353SAdam Hornáček public String getId() { 176b5840353SAdam Hornáček return path; 177b5840353SAdam Hornáček } 178b5840353SAdam Hornáček 179b5840353SAdam Hornáček /** 180b5840353SAdam Hornáček * Get the tab size for this project, if tab size has been set. 181b5840353SAdam Hornáček * 182b5840353SAdam Hornáček * @return tab size if set, 0 otherwise 183b5840353SAdam Hornáček * @see #hasTabSizeSetting() 184b5840353SAdam Hornáček */ getTabSize()185b5840353SAdam Hornáček public int getTabSize() { 186b5840353SAdam Hornáček return tabSize; 187b5840353SAdam Hornáček } 188b5840353SAdam Hornáček 189b5840353SAdam Hornáček /** 190b5840353SAdam Hornáček * Set a textual name of this project, preferably don't use " , " in the 191b5840353SAdam Hornáček * name, since it's used as delimiter for more projects 192b5840353SAdam Hornáček * 193b5840353SAdam Hornáček * XXX we should not allow setting project name after it has been 194b5840353SAdam Hornáček * constructed because it is probably part of HashMap. 195b5840353SAdam Hornáček * 196b5840353SAdam Hornáček * @param name a textual name of the project 197b5840353SAdam Hornáček */ 198b5840353SAdam Hornáček @Override setName(String name)199b5840353SAdam Hornáček public void setName(String name) { 200b5840353SAdam Hornáček this.name = name; 201b5840353SAdam Hornáček } 202b5840353SAdam Hornáček 203b5840353SAdam Hornáček /** 204bd3709daSVladimir Kotal * Set the path (relative from source root) this project is located. 205b5840353SAdam Hornáček * 206b5840353SAdam Hornáček * @param path the relative path from source root where this project is 207bd3709daSVladimir Kotal * located, starting with path separator. 208b5840353SAdam Hornáček */ setPath(String path)209b5840353SAdam Hornáček public void setPath(String path) { 210bd3709daSVladimir Kotal this.path = Util.fixPathIfWindows(path); 211b5840353SAdam Hornáček } 212b5840353SAdam Hornáček setIndexed(boolean flag)213b5840353SAdam Hornáček public void setIndexed(boolean flag) { 214b5840353SAdam Hornáček this.indexed = flag; 215b5840353SAdam Hornáček } 216b5840353SAdam Hornáček 217b5840353SAdam Hornáček /** 218b5840353SAdam Hornáček * Set tab size for this project. Used for expanding tabs to spaces in 219b5840353SAdam Hornáček * xrefs. 220b5840353SAdam Hornáček * 221b5840353SAdam Hornáček * @param tabSize the size of tabs in this project 222b5840353SAdam Hornáček */ setTabSize(int tabSize)223b5840353SAdam Hornáček public void setTabSize(int tabSize) { 224b5840353SAdam Hornáček this.tabSize = tabSize; 225b5840353SAdam Hornáček } 226b5840353SAdam Hornáček 227b5840353SAdam Hornáček /** 228b5840353SAdam Hornáček * Has this project an explicit tab size setting? 229b5840353SAdam Hornáček * 230b5840353SAdam Hornáček * @return {@code true} if the tab size has been set for this project, or 231b5840353SAdam Hornáček * {@code false} if it hasn't and the default should be used 232b5840353SAdam Hornáček */ hasTabSizeSetting()233b5840353SAdam Hornáček public boolean hasTabSizeSetting() { 234b5840353SAdam Hornáček return tabSize > 0; 235b5840353SAdam Hornáček } 236b5840353SAdam Hornáček 237b5840353SAdam Hornáček /** 238b5840353SAdam Hornáček * Indicate whether the navigate window should be opened by default when 239b5840353SAdam Hornáček * browsing a source code from this project. 240b5840353SAdam Hornáček * 241b5840353SAdam Hornáček * @return true if yes; false otherwise 242b5840353SAdam Hornáček */ isNavigateWindowEnabled()243b5840353SAdam Hornáček public boolean isNavigateWindowEnabled() { 244326dace0SVladimir Kotal return navigateWindowEnabled != null && navigateWindowEnabled; 245b5840353SAdam Hornáček } 246b5840353SAdam Hornáček 247b5840353SAdam Hornáček /** 248ff44f24aSAdam Hornáček * Set the value of navigateWindowEnabled. 249b5840353SAdam Hornáček * 250b5840353SAdam Hornáček * @param navigateWindowEnabled new value of navigateWindowEnabled 251b5840353SAdam Hornáček */ setNavigateWindowEnabled(boolean navigateWindowEnabled)252b5840353SAdam Hornáček public void setNavigateWindowEnabled(boolean navigateWindowEnabled) { 253b5840353SAdam Hornáček this.navigateWindowEnabled = navigateWindowEnabled; 254b5840353SAdam Hornáček } 255b5840353SAdam Hornáček 256b5840353SAdam Hornáček /** 257b5840353SAdam Hornáček * @return true if this project handles renamed files. 258b5840353SAdam Hornáček */ isHandleRenamedFiles()259b5840353SAdam Hornáček public boolean isHandleRenamedFiles() { 260b5840353SAdam Hornáček return handleRenamedFiles != null && handleRenamedFiles; 261b5840353SAdam Hornáček } 262b5840353SAdam Hornáček 263b5840353SAdam Hornáček /** 2648e5e1036SVladimir Kotal * @return true if merge commits are enabled. 2658e5e1036SVladimir Kotal */ isMergeCommitsEnabled()2668e5e1036SVladimir Kotal public boolean isMergeCommitsEnabled() { 2678e5e1036SVladimir Kotal return mergeCommitsEnabled != null && mergeCommitsEnabled; 2688e5e1036SVladimir Kotal } 2698e5e1036SVladimir Kotal 2708e5e1036SVladimir Kotal /** 271b5840353SAdam Hornáček * @param flag true if project should handle renamed files, false otherwise. 272b5840353SAdam Hornáček */ setHandleRenamedFiles(boolean flag)273b5840353SAdam Hornáček public void setHandleRenamedFiles(boolean flag) { 274b5840353SAdam Hornáček this.handleRenamedFiles = flag; 275b5840353SAdam Hornáček } 276b5840353SAdam Hornáček 277b5840353SAdam Hornáček /** 278b5840353SAdam Hornáček * @return true if this project should have history cache. 279b5840353SAdam Hornáček */ isHistoryEnabled()280b5840353SAdam Hornáček public boolean isHistoryEnabled() { 281b5840353SAdam Hornáček return historyEnabled != null && historyEnabled; 282b5840353SAdam Hornáček } 283b5840353SAdam Hornáček 284b5840353SAdam Hornáček /** 285b5840353SAdam Hornáček * @param flag true if project should have history cache, false otherwise. 286b5840353SAdam Hornáček */ setHistoryEnabled(boolean flag)287b5840353SAdam Hornáček public void setHistoryEnabled(boolean flag) { 288b5840353SAdam Hornáček this.historyEnabled = flag; 289b5840353SAdam Hornáček } 290b5840353SAdam Hornáček 291b5840353SAdam Hornáček /** 2928e5e1036SVladimir Kotal * @param flag true if project's repositories should deal with merge commits. 2938e5e1036SVladimir Kotal */ setMergeCommitsEnabled(boolean flag)2948e5e1036SVladimir Kotal public void setMergeCommitsEnabled(boolean flag) { 2958e5e1036SVladimir Kotal this.mergeCommitsEnabled = flag; 2968e5e1036SVladimir Kotal } 2978e5e1036SVladimir Kotal 2988e5e1036SVladimir Kotal /** 2997a438acaSVladimir Kotal * @return true if this project handles renamed files. 3007a438acaSVladimir Kotal */ isHistoryBasedReindex()301f4571972SVladimir Kotal public boolean isHistoryBasedReindex() { 302f4571972SVladimir Kotal return historyBasedReindex != null && historyBasedReindex; 3037a438acaSVladimir Kotal } 3047a438acaSVladimir Kotal 3057a438acaSVladimir Kotal /** 3067a438acaSVladimir Kotal * @param flag true if project should handle renamed files, false otherwise. 3077a438acaSVladimir Kotal */ setHistoryBasedReindex(boolean flag)308f4571972SVladimir Kotal public void setHistoryBasedReindex(boolean flag) { 309f4571972SVladimir Kotal this.historyBasedReindex = flag; 3107a438acaSVladimir Kotal } 3117a438acaSVladimir Kotal 312a6a884eaSVladimir Kotal @VisibleForTesting clearProperties()313984a8869SVladimir Kotal public void clearProperties() { 314984a8869SVladimir Kotal historyBasedReindex = null; 315984a8869SVladimir Kotal mergeCommitsEnabled = null; 316984a8869SVladimir Kotal historyEnabled = null; 317984a8869SVladimir Kotal handleRenamedFiles = null; 318a6a884eaSVladimir Kotal } 319a6a884eaSVladimir Kotal 3207a438acaSVladimir Kotal /** 321ff44f24aSAdam Hornáček * Return groups where this project belongs. 322b5840353SAdam Hornáček * 323b5840353SAdam Hornáček * @return set of groups|empty if none 324b5840353SAdam Hornáček */ getGroups()325b5840353SAdam Hornáček public Set<Group> getGroups() { 326b5840353SAdam Hornáček return groups; 327b5840353SAdam Hornáček } 328b5840353SAdam Hornáček setGroups(Set<Group> groups)329b5840353SAdam Hornáček public void setGroups(Set<Group> groups) { 330b5840353SAdam Hornáček this.groups = groups; 331b5840353SAdam Hornáček } 332b5840353SAdam Hornáček 333b5840353SAdam Hornáček /** 334ff44f24aSAdam Hornáček * Adds a group where this project belongs. 335b5840353SAdam Hornáček * 336b5840353SAdam Hornáček * @param group group to add 337b5840353SAdam Hornáček */ addGroup(Group group)338b5840353SAdam Hornáček public void addGroup(Group group) { 339b5840353SAdam Hornáček while (group != null) { 340b5840353SAdam Hornáček this.groups.add(group); 341b5840353SAdam Hornáček group = group.getParent(); 342b5840353SAdam Hornáček } 343b5840353SAdam Hornáček } 344b5840353SAdam Hornáček setBugPage(String bugPage)34564819763SVladimir Kotal public void setBugPage(String bugPage) { 34664819763SVladimir Kotal this.bugPage = bugPage; 34764819763SVladimir Kotal } 34864819763SVladimir Kotal getBugPage()34964819763SVladimir Kotal public String getBugPage() { 35064819763SVladimir Kotal if (bugPage != null) { 35164819763SVladimir Kotal return bugPage; 35264819763SVladimir Kotal } else { 35364819763SVladimir Kotal return RuntimeEnvironment.getInstance().getBugPage(); 35464819763SVladimir Kotal } 35564819763SVladimir Kotal } 35664819763SVladimir Kotal 35764819763SVladimir Kotal /** 35864819763SVladimir Kotal * Set the bug pattern to a new value. 35964819763SVladimir Kotal * 36064819763SVladimir Kotal * @param bugPattern the new pattern 36164819763SVladimir Kotal * @throws PatternSyntaxException when the pattern is not a valid regexp or 36264819763SVladimir Kotal * does not contain at least one capture group and the group does not 36364819763SVladimir Kotal * contain a single character 36464819763SVladimir Kotal */ setBugPattern(String bugPattern)36564819763SVladimir Kotal public void setBugPattern(String bugPattern) throws PatternSyntaxException { 36664819763SVladimir Kotal this.bugPattern = compilePattern(bugPattern); 36764819763SVladimir Kotal } 36864819763SVladimir Kotal getBugPattern()36964819763SVladimir Kotal public String getBugPattern() { 37064819763SVladimir Kotal if (bugPattern != null) { 37164819763SVladimir Kotal return bugPattern; 37264819763SVladimir Kotal } else { 37364819763SVladimir Kotal return RuntimeEnvironment.getInstance().getBugPattern(); 37464819763SVladimir Kotal } 37564819763SVladimir Kotal } 37664819763SVladimir Kotal getReviewPage()37764819763SVladimir Kotal public String getReviewPage() { 37864819763SVladimir Kotal if (reviewPage != null) { 37964819763SVladimir Kotal return reviewPage; 38064819763SVladimir Kotal } else { 38164819763SVladimir Kotal return RuntimeEnvironment.getInstance().getReviewPage(); 38264819763SVladimir Kotal } 38364819763SVladimir Kotal } 38464819763SVladimir Kotal setReviewPage(String reviewPage)38564819763SVladimir Kotal public void setReviewPage(String reviewPage) { 38664819763SVladimir Kotal this.reviewPage = reviewPage; 38764819763SVladimir Kotal } 38864819763SVladimir Kotal getReviewPattern()38964819763SVladimir Kotal public String getReviewPattern() { 39064819763SVladimir Kotal if (reviewPattern != null) { 39164819763SVladimir Kotal return reviewPattern; 39264819763SVladimir Kotal } else { 39364819763SVladimir Kotal return RuntimeEnvironment.getInstance().getReviewPattern(); 39464819763SVladimir Kotal } 39564819763SVladimir Kotal } 39664819763SVladimir Kotal 39764819763SVladimir Kotal /** 39864819763SVladimir Kotal * Set the review pattern to a new value. 39964819763SVladimir Kotal * 40064819763SVladimir Kotal * @param reviewPattern the new pattern 40164819763SVladimir Kotal * @throws PatternSyntaxException when the pattern is not a valid regexp or 40264819763SVladimir Kotal * does not contain at least one capture group and the group does not 40364819763SVladimir Kotal * contain a single character 40464819763SVladimir Kotal */ setReviewPattern(String reviewPattern)40564819763SVladimir Kotal public void setReviewPattern(String reviewPattern) throws PatternSyntaxException { 40664819763SVladimir Kotal this.reviewPattern = compilePattern(reviewPattern); 40764819763SVladimir Kotal } 40864819763SVladimir Kotal 409b5840353SAdam Hornáček /** 410b5840353SAdam Hornáček * Fill the project with the current configuration where the applicable 411b5840353SAdam Hornáček * project property has a default value. 412b5840353SAdam Hornáček */ completeWithDefaults()413d1e826faSAdam Hornáček public final void completeWithDefaults() { 414b5840353SAdam Hornáček Configuration defaultCfg = new Configuration(); 4152ffbb0cfSVladimir Kotal final RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 4162ffbb0cfSVladimir Kotal 41764819763SVladimir Kotal /* 418b5840353SAdam Hornáček * Choosing strategy for properties (tabSize used as example here): 419b5840353SAdam Hornáček * <pre> 420b5840353SAdam Hornáček * this cfg defaultCfg chosen value 421b5840353SAdam Hornáček * =============================================== 422b5840353SAdam Hornáček * |5| 4 0 5 423b5840353SAdam Hornáček * 0 |4| 0 4 424b5840353SAdam Hornáček * </pre> 425b5840353SAdam Hornáček * 426b5840353SAdam Hornáček * The strategy is: 427b5840353SAdam Hornáček * 1) if the project has some non-default value; use that 428b5840353SAdam Hornáček * 2) if the project has a default value; use the provided configuration 429b5840353SAdam Hornáček */ 430b5840353SAdam Hornáček if (getTabSize() == defaultCfg.getTabSize()) { 4312ffbb0cfSVladimir Kotal setTabSize(env.getTabSize()); 432b5840353SAdam Hornáček } 433b5840353SAdam Hornáček 434b5840353SAdam Hornáček // Allow project to override global setting of renamed file handling. 435b5840353SAdam Hornáček if (handleRenamedFiles == null) { 4362ffbb0cfSVladimir Kotal setHandleRenamedFiles(env.isHandleHistoryOfRenamedFiles()); 437b5840353SAdam Hornáček } 438b5840353SAdam Hornáček 439b5840353SAdam Hornáček // Allow project to override global setting of history cache generation. 440b5840353SAdam Hornáček if (historyEnabled == null) { 4412ffbb0cfSVladimir Kotal setHistoryEnabled(env.isHistoryEnabled()); 442b5840353SAdam Hornáček } 443326dace0SVladimir Kotal 444326dace0SVladimir Kotal // Allow project to override global setting of navigate window. 445326dace0SVladimir Kotal if (navigateWindowEnabled == null) { 4462ffbb0cfSVladimir Kotal setNavigateWindowEnabled(env.isNavigateWindowEnabled()); 447326dace0SVladimir Kotal } 4488e5e1036SVladimir Kotal 4498e5e1036SVladimir Kotal // Allow project to override global setting of merge commits. 4508e5e1036SVladimir Kotal if (mergeCommitsEnabled == null) { 4518e5e1036SVladimir Kotal setMergeCommitsEnabled(env.isMergeCommitsEnabled()); 4528e5e1036SVladimir Kotal } 45364819763SVladimir Kotal 45464819763SVladimir Kotal if (bugPage == null) { 45564819763SVladimir Kotal setBugPage(env.getBugPage()); 45664819763SVladimir Kotal } 45764819763SVladimir Kotal if (bugPattern == null) { 45864819763SVladimir Kotal setBugPattern(env.getBugPattern()); 45964819763SVladimir Kotal } 46064819763SVladimir Kotal 46164819763SVladimir Kotal if (reviewPage == null) { 46264819763SVladimir Kotal setReviewPage(env.getReviewPage()); 46364819763SVladimir Kotal } 46464819763SVladimir Kotal if (reviewPattern == null) { 46564819763SVladimir Kotal setReviewPattern(env.getReviewPattern()); 46664819763SVladimir Kotal } 4677a438acaSVladimir Kotal 468f4571972SVladimir Kotal if (historyBasedReindex == null) { 469f4571972SVladimir Kotal setHistoryBasedReindex(env.isHistoryBasedReindex()); 4707a438acaSVladimir Kotal } 471b5840353SAdam Hornáček } 472b5840353SAdam Hornáček 473b5840353SAdam Hornáček /** 474ff44f24aSAdam Hornáček * Get the project for a specific file. 475b5840353SAdam Hornáček * 476b5840353SAdam Hornáček * @param path the file to lookup (relative to source root) 477b5840353SAdam Hornáček * @return the project that this file belongs to (or null if the file 478b5840353SAdam Hornáček * doesn't belong to a project) 479b5840353SAdam Hornáček */ getProject(String path)480b5840353SAdam Hornáček public static Project getProject(String path) { 481b5840353SAdam Hornáček // Try to match each project path as prefix of the given path. 482b5840353SAdam Hornáček final RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 483b5840353SAdam Hornáček if (env.hasProjects()) { 484807ead8fSLubos Kosco final String lpath = Util.fixPathIfWindows(path); 485b5840353SAdam Hornáček for (Project p : env.getProjectList()) { 4866b287e05SVladimir Kotal String projectPath = p.getPath(); 4876b287e05SVladimir Kotal if (projectPath == null) { 4886b287e05SVladimir Kotal LOGGER.log(Level.WARNING, "Path of project {0} is not set", p.getName()); 4896b287e05SVladimir Kotal return null; 4906b287e05SVladimir Kotal } 4916b287e05SVladimir Kotal 492b5840353SAdam Hornáček // Check if the project's path is a prefix of the given 493b5840353SAdam Hornáček // path. It has to be an exact match, or the project's path 494b5840353SAdam Hornáček // must be immediately followed by a separator. "/foo" is 495b5840353SAdam Hornáček // a prefix for "/foo" and "/foo/bar", but not for "/foof". 4966b287e05SVladimir Kotal if (lpath.startsWith(projectPath) 4976b287e05SVladimir Kotal && (projectPath.length() == lpath.length() 4986b287e05SVladimir Kotal || lpath.charAt(projectPath.length()) == '/')) { 499b5840353SAdam Hornáček return p; 500b5840353SAdam Hornáček } 501b5840353SAdam Hornáček } 502b5840353SAdam Hornáček } 503b5840353SAdam Hornáček 504b5840353SAdam Hornáček return null; 505b5840353SAdam Hornáček } 506b5840353SAdam Hornáček 507b5840353SAdam Hornáček /** 508ff44f24aSAdam Hornáček * Get the project for a specific file. 509b5840353SAdam Hornáček * 510b5840353SAdam Hornáček * @param file the file to lookup 511a6a884eaSVladimir Kotal * @return the project that this file belongs to (or {@code null} if the file doesn't belong to a project) 512b5840353SAdam Hornáček */ getProject(File file)513b5840353SAdam Hornáček public static Project getProject(File file) { 514b5840353SAdam Hornáček Project ret = null; 515b5840353SAdam Hornáček try { 516b5840353SAdam Hornáček ret = getProject(RuntimeEnvironment.getInstance().getPathRelativeToSourceRoot(file)); 517b5840353SAdam Hornáček } catch (FileNotFoundException e) { // NOPMD 518b5840353SAdam Hornáček // ignore if not under source root 519b5840353SAdam Hornáček } catch (ForbiddenSymlinkException e) { 520b5840353SAdam Hornáček LOGGER.log(Level.FINER, e.getMessage()); 521b5840353SAdam Hornáček // ignore 522b5840353SAdam Hornáček } catch (IOException e) { // NOPMD 523b5840353SAdam Hornáček // problem has already been logged, just return null 524b5840353SAdam Hornáček } 525b5840353SAdam Hornáček return ret; 526b5840353SAdam Hornáček } 527b5840353SAdam Hornáček 528b5840353SAdam Hornáček /** 529b5840353SAdam Hornáček * Returns project object by its name, used in webapp to figure out which 530ff44f24aSAdam Hornáček * project is to be searched. 531b5840353SAdam Hornáček * 532b5840353SAdam Hornáček * @param name name of the project 533b5840353SAdam Hornáček * @return project that fits the name 534b5840353SAdam Hornáček */ getByName(String name)535b5840353SAdam Hornáček public static Project getByName(String name) { 536b5840353SAdam Hornáček RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 537b5840353SAdam Hornáček if (env.hasProjects()) { 538b5840353SAdam Hornáček Project proj; 539b5840353SAdam Hornáček if ((proj = env.getProjects().get(name)) != null) { 540b5840353SAdam Hornáček return (proj); 541b5840353SAdam Hornáček } 542b5840353SAdam Hornáček } 543b5840353SAdam Hornáček return null; 544b5840353SAdam Hornáček } 545b5840353SAdam Hornáček 546b5840353SAdam Hornáček @Override compareTo(Project p2)547b5840353SAdam Hornáček public int compareTo(Project p2) { 54852dccac1SChris Fraire return getName().toUpperCase(Locale.ROOT).compareTo( 54952dccac1SChris Fraire p2.getName().toUpperCase(Locale.ROOT)); 550b5840353SAdam Hornáček } 551b5840353SAdam Hornáček 552b5840353SAdam Hornáček @Override hashCode()553b5840353SAdam Hornáček public int hashCode() { 554b5840353SAdam Hornáček int hash = 3; 55552dccac1SChris Fraire hash = 41 * hash + (this.name == null ? 0 : 55652dccac1SChris Fraire this.name.toUpperCase(Locale.ROOT).hashCode()); 557b5840353SAdam Hornáček return hash; 558b5840353SAdam Hornáček } 559b5840353SAdam Hornáček 560b5840353SAdam Hornáček @Override equals(Object obj)561b5840353SAdam Hornáček public boolean equals(Object obj) { 562b5840353SAdam Hornáček if (this == obj) { 563b5840353SAdam Hornáček return true; 564b5840353SAdam Hornáček } 565b5840353SAdam Hornáček if (obj == null) { 566b5840353SAdam Hornáček return false; 567b5840353SAdam Hornáček } 568b5840353SAdam Hornáček if (getClass() != obj.getClass()) { 569b5840353SAdam Hornáček return false; 570b5840353SAdam Hornáček } 571b5840353SAdam Hornáček final Project other = (Project) obj; 57252dccac1SChris Fraire 57352dccac1SChris Fraire int numNull = (name == null ? 1 : 0) + (other.name == null ? 1 : 0); 57452dccac1SChris Fraire switch (numNull) { 57552dccac1SChris Fraire case 0: 57652dccac1SChris Fraire return name.toUpperCase(Locale.ROOT).equals( 57752dccac1SChris Fraire other.name.toUpperCase(Locale.ROOT)); 57852dccac1SChris Fraire case 1: 57952dccac1SChris Fraire return false; 58052dccac1SChris Fraire default: 58152dccac1SChris Fraire return true; 58252dccac1SChris Fraire } 583b5840353SAdam Hornáček } 584*76ceaf0eSVladimir Kotal 585*76ceaf0eSVladimir Kotal @Override toString()586*76ceaf0eSVladimir Kotal public String toString() { 587*76ceaf0eSVladimir Kotal return getName() + ":indexed=" + isIndexed() + ",history=" + isHistoryEnabled(); 588*76ceaf0eSVladimir Kotal } 589b5840353SAdam Hornáček } 590