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) 2007, 2021, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2019, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.history; 25 26 import org.jetbrains.annotations.Nullable; 27 28 import java.io.Serializable; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.Set; 37 import java.util.stream.Collectors; 38 39 /** 40 * Class representing the history of a file. 41 */ 42 public class History implements Serializable { 43 44 private static final long serialVersionUID = -1; 45 46 static final String TAGS_SEPARATOR = ", "; 47 48 /** Entries in the log. The first entry is the most recent one. */ 49 private List<HistoryEntry> entries; 50 /** 51 * track renamed files so they can be treated in special way (for some 52 * SCMs) during cache creation. 53 * These are relative to repository root. 54 */ 55 private final Set<String> renamedFiles; 56 57 // revision to tag list. Individual tags are joined via TAGS_SEPARATOR. 58 private Map<String, String> tags = new HashMap<>(); 59 History()60 public History() { 61 this(new ArrayList<>()); 62 } 63 History(List<HistoryEntry> entries)64 History(List<HistoryEntry> entries) { 65 this(entries, Collections.emptyList()); 66 } 67 History(List<HistoryEntry> entries, List<String> renamed)68 History(List<HistoryEntry> entries, List<String> renamed) { 69 this.entries = entries; 70 this.renamedFiles = new HashSet<>(renamed); 71 } 72 History(List<HistoryEntry> entries, Set<String> renamed)73 History(List<HistoryEntry> entries, Set<String> renamed) { 74 this.entries = entries; 75 this.renamedFiles = renamed; 76 } 77 78 // Needed for serialization. getTags()79 public Map<String, String> getTags() { 80 return tags; 81 } 82 83 // Needed for serialization. setTags(Map<String, String> tags)84 public void setTags(Map<String, String> tags) { 85 this.tags = tags; 86 } 87 setEntries(List<HistoryEntry> entries)88 public void setEntries(List<HistoryEntry> entries) { 89 this.entries = entries; 90 } 91 92 /** 93 * Set the list of log entries for the file. The first entry is the most 94 * recent one. 95 * 96 * @param entries The entries to add to the list 97 */ setHistoryEntries(List<HistoryEntry> entries)98 public void setHistoryEntries(List<HistoryEntry> entries) { 99 this.entries = entries; 100 } 101 102 /** 103 * Get the list of log entries, most recent first. 104 * 105 * @return The list of entries in this history 106 */ getHistoryEntries()107 public List<HistoryEntry> getHistoryEntries() { 108 return entries; 109 } 110 111 /** 112 * Get the list of log entries, most recent first. 113 * With parameters 114 * @param limit max number of entries 115 * @param offset starting position 116 * 117 * @return The list of entries in this history 118 */ getHistoryEntries(int limit, int offset)119 public List<HistoryEntry> getHistoryEntries(int limit, int offset) { 120 offset = Math.max(offset, 0); 121 limit = offset + limit > entries.size() ? entries.size() - offset : limit; 122 return entries.subList(offset, offset + limit); 123 } 124 125 /** 126 * Check if at least one history entry has a file list. 127 * 128 * @return {@code true} if at least one of the entries has a non-empty 129 * file list, {@code false} otherwise 130 */ hasFileList()131 public boolean hasFileList() { 132 return entries.stream() 133 .map(HistoryEntry::getFiles) 134 .anyMatch(files -> !files.isEmpty()); 135 } 136 137 /** 138 * Check if at least one history entry has a tag list. 139 * 140 * @return {@code true} if at least one of the entries has a non-empty 141 * tag list, {@code false} otherwise 142 */ hasTags()143 public boolean hasTags() { 144 return !tags.isEmpty(); 145 } 146 addTags(HistoryEntry entry, String newTags)147 public void addTags(HistoryEntry entry, String newTags) { 148 tags.merge(entry.getRevision(), newTags, (a, b) -> a + TAGS_SEPARATOR + b); 149 } 150 151 /** 152 * Gets a value indicating if {@code file} is in the list of renamed files. 153 * @param file file path 154 * @return is file renamed 155 */ isRenamed(String file)156 public boolean isRenamed(String file) { 157 return renamedFiles.contains(file); 158 } 159 getRenamedFiles()160 public Set<String> getRenamedFiles() { 161 return renamedFiles; 162 } 163 164 /** 165 * @return list of revisions 166 */ getRevisionList()167 public List<String> getRevisionList() { 168 return getHistoryEntries().stream(). 169 map(HistoryEntry::getRevision).collect(Collectors.toList()); 170 } 171 172 /** 173 * Strip files and tags. 174 * @see HistoryEntry#strip() 175 */ strip()176 public void strip() { 177 for (HistoryEntry ent : this.getHistoryEntries()) { 178 ent.strip(); 179 } 180 181 tags.clear(); 182 } 183 184 /** 185 * @return last (newest) history entry or null 186 */ getLastHistoryEntry()187 public @Nullable HistoryEntry getLastHistoryEntry() { 188 List<HistoryEntry> historyEntries = getHistoryEntries(); 189 if (historyEntries == null || historyEntries.isEmpty()) { 190 return null; 191 } 192 193 return historyEntries.get(0); 194 } 195 196 @Override equals(Object o)197 public boolean equals(Object o) { 198 if (this == o) { 199 return true; 200 } 201 if (o == null || getClass() != o.getClass()) { 202 return false; 203 } 204 History that = (History) o; 205 return Objects.equals(this.getHistoryEntries(), that.getHistoryEntries()) && 206 Objects.equals(this.getTags(), that.getTags()) && 207 Objects.equals(this.getRenamedFiles(), that.getRenamedFiles()); 208 } 209 210 @Override hashCode()211 public int hashCode() { 212 return Objects.hash(getHistoryEntries(), getTags(), getRenamedFiles()); 213 } 214 215 @Override toString()216 public String toString() { 217 return this.getHistoryEntries().toString() + ", renamed files: " + this.getRenamedFiles().toString() + 218 " , tags: " + getTags(); 219 } 220 } 221