001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.database; 019 020import java.util.Collections; 021import java.util.Map; 022import java.util.TreeMap; 023 024/** 025 * A <code>PartialFileHistory</code> represents a single file in a repository over a 026 * certain period of time/versions. Whenever a file is updated or deleted, a new 027 * {@link FileVersion} is added to the file history. 028 * 029 * <p>A file history is identified by a unique random identifier and holds a sorted 030 * list of file versions. 031 * 032 * <p>Due to cleanup mechanisms and the delta database concept, the list of file 033 * versions is not always complete. The class hence represents a part of the file 034 * history. 035 * 036 * @see FileVersion 037 * @author Philipp C. Heckel (philipp.heckel@gmail.com) 038 * @author Fabrice Rossi 039 */ 040public class PartialFileHistory { 041 private static final byte FILE_HISTORY_ID_LENGTH = 20; 042 043 private FileHistoryId fileHistoryId; 044 private TreeMap<Long, FileVersion> versions; 045 046 public PartialFileHistory() { 047 // Required for SimpleXML. 048 } 049 050 /** 051 * Creates a new file history instance, given a {@link FileHistoryId} as identifier 052 * of the file over time. After creation, the file history's versions map is empty. 053 * 054 * @param fileHistoryId Random or non-random file history identifier 055 * @throws IllegalArgumentException If fileHistoryId is null 056 */ 057 public PartialFileHistory(FileHistoryId fileHistoryId) { 058 if (fileHistoryId == null) { 059 throw new IllegalArgumentException("Argument fileHistoryId cannot be null."); 060 } 061 062 this.fileHistoryId = fileHistoryId; 063 this.versions = new TreeMap<Long, FileVersion>(); 064 } 065 066 /** 067 * Returns the file history identifier for this file history. Note that 068 * this value cannot be null. 069 */ 070 public FileHistoryId getFileHistoryId() { 071 return fileHistoryId; 072 } 073 074 /** 075 * Returns an unmodifiable map of the {@link FileVersion}s, keyed by the 076 * version number of the corresponding file version. 077 */ 078 public Map<Long, FileVersion> getFileVersions() { 079 return Collections.unmodifiableMap(versions); 080 } 081 082 /** 083 * Returns the file version with the given file version number, or null if 084 * a version with this number does not exist in this file history. 085 */ 086 public FileVersion getFileVersion(long version) { 087 return versions.get(version); 088 } 089 090 /** 091 * Returns the last file version in this instance of the partial file history, 092 * or <code>null</code> if there are no file versions. 093 * 094 * <p>Note that this method does not necessarily return the actual overall 095 * last file version, only the last of this object instance. 096 * 097 * @return Returns the last file version, or <code>null</code> 098 */ 099 public FileVersion getLastVersion() { 100 if (versions.isEmpty()) { 101 return null; 102 } 103 104 return versions.lastEntry().getValue(); 105 } 106 107 /** 108 * Adds a new file version of the file history. The given file version is added 109 * to an internal tree map, sorted by the attribute {@link FileVersion#getVersion()}. 110 * 111 * If a file version version with the same version already exists, it is replaced by 112 * the given file version. 113 * 114 * @param fileVersion File version to be added to the file history 115 * @throws IllegalArgumentException If fileVersion or its version number is <code>null</code> 116 */ 117 public void addFileVersion(FileVersion fileVersion) { 118 if (fileVersion == null || fileVersion.getVersion() == null) { 119 throw new IllegalArgumentException("Argument fileVersion or fileVersion.getVersion() cannot be null."); 120 } 121 122 versions.put(fileVersion.getVersion(), fileVersion); 123 } 124 125 /** 126 * Clones the file history, including its file versions. Note that file versions 127 * are not cloned, but copied by reference. 128 * 129 * @return Returns cloned file history 130 */ 131 @Override 132 public PartialFileHistory clone() { 133 PartialFileHistory clone = new PartialFileHistory(fileHistoryId); 134 clone.versions.putAll(versions); 135 136 return clone; 137 } 138 139 @Override 140 public String toString() { 141 return PartialFileHistory.class.getSimpleName() + "(fileId=" + fileHistoryId + ", versions=" + versions + ")"; 142 } 143 144 @Override 145 public int hashCode() { 146 final int prime = 31; 147 int result = 1; 148 result = prime * result + ((fileHistoryId == null) ? 0 : fileHistoryId.hashCode()); 149 result = prime * result + ((versions == null) ? 0 : versions.hashCode()); 150 return result; 151 } 152 153 @Override 154 public boolean equals(Object obj) { 155 if (this == obj) { 156 return true; 157 } 158 if (obj == null) { 159 return false; 160 } 161 if (!(obj instanceof PartialFileHistory)) { 162 return false; 163 } 164 PartialFileHistory other = (PartialFileHistory) obj; 165 if (fileHistoryId == null) { 166 if (other.fileHistoryId != null) { 167 return false; 168 } 169 } 170 else if (!fileHistoryId.equals(other.fileHistoryId)) { 171 return false; 172 } 173 if (versions == null) { 174 if (other.versions != null) { 175 return false; 176 } 177 } 178 else if (!versions.equals(other.versions)) { 179 return false; 180 } 181 return true; 182 } 183 184 /** 185 * The file history identifier (also: file identifier) is a key to identify a single file 186 * throughout its lifetime. In particular, it does not only identify 187 * 188 */ 189 public static class FileHistoryId extends ObjectId { 190 private FileHistoryId(byte[] array) { 191 super(array); 192 } 193 194 public static FileHistoryId secureRandomFileId() { 195 return new FileHistoryId(ObjectId.secureRandomBytes(FILE_HISTORY_ID_LENGTH)); 196 } 197 198 public static FileHistoryId parseFileId(String s) { 199 return new FileHistoryId(ObjectId.parseObjectId(s)); 200 } 201 } 202}