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) 2008, 2022, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2017, 2019, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.history; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.io.Serializable; 29 import java.nio.file.Paths; 30 import java.util.Objects; 31 import java.util.logging.Level; 32 import java.util.logging.Logger; 33 34 import org.opengrok.indexer.configuration.Project; 35 import org.opengrok.indexer.configuration.RuntimeEnvironment; 36 import org.opengrok.indexer.logger.LoggerFactory; 37 import org.opengrok.indexer.util.ClassUtil; 38 import org.opengrok.indexer.util.DTOElement; 39 import org.opengrok.indexer.util.PathUtils; 40 41 /** 42 * Class to contain the common info for a repository. This object will live on 43 * the server and the client side, so don't add logic that will only work on one 44 * side in this object. 45 * 46 * @author Trond Norbye 47 */ 48 public class RepositoryInfo implements Serializable { 49 50 private static final Logger LOGGER = 51 LoggerFactory.getLogger(RepositoryInfo.class); 52 53 static { 54 ClassUtil.remarkTransientFields(RepositoryInfo.class); 55 } 56 57 private static final long serialVersionUID = 3L; 58 59 @DTOElement 60 private String directoryNameRelative; 61 private transient String directoryNameCanonical; 62 63 @DTOElement 64 protected Boolean working; 65 @DTOElement 66 protected String type; // type of the repository, should be unique 67 @DTOElement 68 protected boolean remote; 69 protected String[] datePatterns = new String[0]; 70 @DTOElement 71 protected String parent; 72 @DTOElement 73 protected String branch; 74 @DTOElement 75 protected String currentVersion; 76 @DTOElement 77 private boolean handleRenamedFiles; 78 @DTOElement 79 private boolean historyEnabled; 80 @DTOElement 81 private boolean mergeCommitsEnabled; 82 @DTOElement 83 private boolean historyBasedReindex; 84 85 /** 86 * Empty constructor to support serialization. 87 */ RepositoryInfo()88 public RepositoryInfo() { 89 super(); 90 } 91 RepositoryInfo(RepositoryInfo orig)92 public RepositoryInfo(RepositoryInfo orig) { 93 this.directoryNameRelative = orig.directoryNameRelative; 94 this.type = orig.type; 95 this.working = orig.isWorking(); 96 this.remote = orig.isRemote(); 97 this.datePatterns = orig.datePatterns; 98 this.parent = orig.parent; 99 this.branch = orig.branch; 100 this.currentVersion = orig.currentVersion; 101 this.historyEnabled = orig.historyEnabled; 102 this.handleRenamedFiles = orig.handleRenamedFiles; 103 this.mergeCommitsEnabled = orig.mergeCommitsEnabled; 104 this.historyBasedReindex = orig.historyBasedReindex; 105 } 106 107 /** 108 * @return true if the repository handles renamed files, false otherwise. 109 */ isHandleRenamedFiles()110 public boolean isHandleRenamedFiles() { 111 return this.handleRenamedFiles; 112 } 113 114 /** 115 * @param flag true if the repository should handle renamed files, false otherwise. 116 */ setHandleRenamedFiles(boolean flag)117 public void setHandleRenamedFiles(boolean flag) { 118 this.handleRenamedFiles = flag; 119 } 120 121 /** 122 * @return true if the repository handles merge commits. 123 */ isMergeCommitsEnabled()124 public boolean isMergeCommitsEnabled() { 125 return this.mergeCommitsEnabled; 126 } 127 128 /** 129 * @param flag true if the repository should handle merge commits, false otherwise. 130 */ setMergeCommitsEnabled(boolean flag)131 public void setMergeCommitsEnabled(boolean flag) { 132 this.mergeCommitsEnabled = flag; 133 } 134 135 /** 136 * @return true if history based reindex is enabled for the repository, false otherwise 137 */ isHistoryBasedReindex()138 public boolean isHistoryBasedReindex() { 139 return this.historyBasedReindex; 140 } 141 142 /** 143 * @param flag if history based reindex should be enabled for the repository 144 */ setHistoryBasedReindex(boolean flag)145 public void setHistoryBasedReindex(boolean flag) { 146 this.historyBasedReindex = flag; 147 } 148 149 /** 150 * @return true if the repository should have history cache. 151 */ isHistoryEnabled()152 public boolean isHistoryEnabled() { 153 return this.historyEnabled; 154 } 155 setHistoryEnabled(boolean flag)156 public void setHistoryEnabled(boolean flag) { 157 this.historyEnabled = flag; 158 } 159 160 /** 161 * @return relative path to source root 162 */ getDirectoryNameRelative()163 public String getDirectoryNameRelative() { 164 return directoryNameRelative; 165 } 166 167 /** 168 * Set relative path to source root. 169 * @param dir directory 170 */ setDirectoryNameRelative(String dir)171 public void setDirectoryNameRelative(String dir) { 172 this.directoryNameRelative = dir; 173 this.directoryNameCanonical = null; 174 } 175 176 /** 177 * Get the name of the root directory for this repository. 178 * 179 * @return the name of the root directory 180 */ getDirectoryName()181 public String getDirectoryName() { 182 return Paths.get(RuntimeEnvironment.getInstance().getSourceRootPath(), 183 directoryNameRelative).toString(); 184 } 185 186 /** 187 * Get the canonical {@link #getDirectoryName()} of the root directory for 188 * this repository. 189 */ getCanonicalDirectoryName()190 String getCanonicalDirectoryName() throws IOException { 191 if (directoryNameCanonical == null) { 192 directoryNameCanonical = new File(getDirectoryName()).getCanonicalPath(); 193 } 194 return directoryNameCanonical; 195 } 196 197 /** 198 * Specify the name of the root directory for this repository. 199 * 200 * @param dir the new root directory 201 */ setDirectoryName(File dir)202 public void setDirectoryName(File dir) { 203 RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 204 String rootPath = env.getSourceRootPath(); 205 String path; 206 String originalPath = dir.getPath(); 207 try { 208 path = PathUtils.getRelativeToCanonical(dir.toPath(), Paths.get(rootPath)); 209 // OpenGrok has a weird convention that directoryNameRelative must start with a path separator, 210 // as it is elsewhere directly appended to env.getSourceRootPath() and also stored as such. 211 if (!path.equals(originalPath)) { 212 path = File.separator + path; 213 } 214 } catch (IOException e) { 215 path = originalPath; 216 LOGGER.log(Level.SEVERE, String.format("Failed to get canonical path for %s", path), e); 217 } 218 219 if (path.startsWith(rootPath)) { 220 setDirectoryNameRelative(path.substring(rootPath.length())); 221 } else { 222 setDirectoryNameRelative(path); 223 } 224 } 225 226 /** 227 * Returns true if this repository is usable in this context (for SCM 228 * systems that use external binaries, the binary must be available etc). 229 * 230 * @return true if the HistoryGuru may use the repository 231 */ isWorking()232 public boolean isWorking() { 233 return working != null && working; 234 } 235 236 /** 237 * Set the property working. 238 * 239 * @param working is repository working 240 */ setWorking(Boolean working)241 public void setWorking(Boolean working) { 242 this.working = working; 243 } 244 245 /** 246 * Is the history and version information for this repository stored on a 247 * remote server? 248 * 249 * @return true if the history is stored on a remote server. 250 */ isRemote()251 public boolean isRemote() { 252 return remote; 253 } 254 255 /** 256 * Set the property remote. 257 * 258 * @param remote is remote repository 259 */ setRemote(boolean remote)260 public void setRemote(boolean remote) { 261 this.remote = remote; 262 } 263 264 /** 265 * Get property type. 266 * 267 * @return type 268 */ getType()269 public String getType() { 270 return type; 271 } 272 273 /** 274 * Set property type. 275 * 276 * @param type repository type 277 */ setType(String type)278 public void setType(String type) { 279 this.type = type; 280 } 281 282 /** 283 * Get property parent. 284 * 285 * @return parent 286 */ getParent()287 public String getParent() { 288 return parent; 289 } 290 291 /** 292 * Set property parent. 293 * 294 * @param parent parent of the repository 295 */ setParent(String parent)296 public void setParent(String parent) { 297 this.parent = parent; 298 } 299 setDatePatterns(String[] datePatterns)300 public void setDatePatterns(String[] datePatterns) { 301 this.datePatterns = datePatterns; 302 } 303 getDatePatterns()304 public String[] getDatePatterns() { 305 return datePatterns; 306 } 307 getBranch()308 public String getBranch() { 309 return branch; 310 } 311 setBranch(String branch)312 public void setBranch(String branch) { 313 this.branch = branch; 314 } 315 getCurrentVersion()316 public String getCurrentVersion() { 317 return currentVersion; 318 } 319 setCurrentVersion(String currentVersion)320 public void setCurrentVersion(String currentVersion) { 321 this.currentVersion = currentVersion; 322 } 323 324 /** 325 * Fill configurable properties from associated project (if any) or Configuration. 326 */ fillFromProject()327 public void fillFromProject() { 328 Project proj = Project.getProject(getDirectoryNameRelative()); 329 if (proj != null) { 330 setHistoryEnabled(proj.isHistoryEnabled()); 331 setHandleRenamedFiles(proj.isHandleRenamedFiles()); 332 setMergeCommitsEnabled(proj.isMergeCommitsEnabled()); 333 setHistoryBasedReindex(proj.isHistoryBasedReindex()); 334 } else { 335 RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 336 337 setHistoryEnabled(env.isHistoryEnabled()); 338 setHandleRenamedFiles(env.isHandleHistoryOfRenamedFiles()); 339 setMergeCommitsEnabled(env.isMergeCommitsEnabled()); 340 setHistoryBasedReindex(env.isHistoryBasedReindex()); 341 } 342 } 343 344 @Override equals(Object obj)345 public boolean equals(Object obj) { 346 if (!(obj instanceof RepositoryInfo)) { 347 return false; 348 } 349 350 RepositoryInfo ri = (RepositoryInfo) obj; 351 352 // Directory paths should be unique. 353 if (ri.getDirectoryNameRelative() != null && this.getDirectoryNameRelative() != null) { 354 return ri.getDirectoryNameRelative().equals(this.getDirectoryNameRelative()); 355 } else { 356 return (ri.getDirectoryNameRelative() == null && this.getDirectoryNameRelative() == null); 357 } 358 } 359 360 @Override hashCode()361 public int hashCode() { 362 int hash = 7; 363 hash = 89 * hash + Objects.hashCode(this.directoryNameRelative); 364 return hash; 365 } 366 367 @Override toString()368 public String toString() { 369 StringBuilder stringBuilder = new StringBuilder(); 370 stringBuilder.append("{"); 371 stringBuilder.append("dir="); 372 stringBuilder.append(this.getDirectoryName()); 373 stringBuilder.append(","); 374 stringBuilder.append("type="); 375 stringBuilder.append(getType()); 376 stringBuilder.append(","); 377 378 if (!isHistoryEnabled()) { 379 stringBuilder.append("history=off"); 380 } else { 381 stringBuilder.append("history=on,"); 382 stringBuilder.append("renamed="); 383 stringBuilder.append(this.isHandleRenamedFiles()); 384 stringBuilder.append(","); 385 stringBuilder.append("merge="); 386 stringBuilder.append(this.isMergeCommitsEnabled()); 387 } 388 stringBuilder.append("}"); 389 return stringBuilder.toString(); 390 } 391 } 392