xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Project.java (revision 76ceaf0eb6c53d5570ac2c2b2575263528a63eae)
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