Mercurial > hg > LGDataverses
comparison src/main/java/edu/harvard/iq/dataverse/DataFile.java @ 10:a50cf11e5178
Rewrite LGDataverse completely upgrading to dataverse4.0
| author | Zoe Hong <zhong@mpiwg-berlin.mpg.de> |
|---|---|
| date | Tue, 08 Sep 2015 17:00:21 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 9:5926d6419569 | 10:a50cf11e5178 |
|---|---|
| 1 package edu.harvard.iq.dataverse; | |
| 2 | |
| 3 import edu.harvard.iq.dataverse.DatasetVersion.VersionState; | |
| 4 import edu.harvard.iq.dataverse.api.WorldMapRelatedData; | |
| 5 import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; | |
| 6 import edu.harvard.iq.dataverse.dataaccess.DataAccess; | |
| 7 import edu.harvard.iq.dataverse.dataaccess.DataAccessObject; | |
| 8 import edu.harvard.iq.dataverse.ingest.IngestReport; | |
| 9 import edu.harvard.iq.dataverse.ingest.IngestRequest; | |
| 10 import edu.harvard.iq.dataverse.util.FileUtil; | |
| 11 import edu.harvard.iq.dataverse.util.ShapefileHandler; | |
| 12 import java.io.IOException; | |
| 13 import java.util.List; | |
| 14 import java.util.ArrayList; | |
| 15 import java.util.Objects; | |
| 16 import java.nio.file.Path; | |
| 17 import java.nio.file.Paths; | |
| 18 import java.nio.file.Files; | |
| 19 import javax.persistence.Entity; | |
| 20 import javax.persistence.OneToMany; | |
| 21 import javax.persistence.OneToOne; | |
| 22 import javax.persistence.CascadeType; | |
| 23 import javax.persistence.Column; | |
| 24 import javax.persistence.JoinColumn; | |
| 25 import javax.persistence.JoinTable; | |
| 26 import javax.persistence.ManyToMany; | |
| 27 import javax.persistence.NamedQueries; | |
| 28 import javax.persistence.NamedQuery; | |
| 29 import javax.validation.constraints.Pattern; | |
| 30 import org.hibernate.validator.constraints.NotBlank; | |
| 31 | |
| 32 /** | |
| 33 * | |
| 34 * @author gdurand | |
| 35 */ | |
| 36 @NamedQueries({ | |
| 37 @NamedQuery( name="DataFile.removeFromDatasetVersion", | |
| 38 query="DELETE FROM FileMetadata f WHERE f.datasetVersion.id=:versionId and f.dataFile.id=:fileId") | |
| 39 }) | |
| 40 @Entity | |
| 41 public class DataFile extends DvObject { | |
| 42 private static final long serialVersionUID = 1L; | |
| 43 | |
| 44 public static final char INGEST_STATUS_NONE = 65; | |
| 45 public static final char INGEST_STATUS_SCHEDULED = 66; | |
| 46 public static final char INGEST_STATUS_INPROGRESS = 67; | |
| 47 public static final char INGEST_STATUS_ERROR = 68; | |
| 48 | |
| 49 private String name; | |
| 50 | |
| 51 @NotBlank | |
| 52 @Column( nullable = false ) | |
| 53 @Pattern(regexp = "^.*/.*$", message = "Content-Type must contain a slash") | |
| 54 private String contentType; | |
| 55 | |
| 56 @Column( nullable = false ) | |
| 57 private String fileSystemName; | |
| 58 | |
| 59 @Column( nullable = false ) | |
| 60 private String md5; | |
| 61 | |
| 62 @Column(nullable=true) | |
| 63 private Long filesize; // Number of bytes in file. Allows 0 and null, negative numbers not permitted | |
| 64 | |
| 65 private boolean restricted; | |
| 66 | |
| 67 /* | |
| 68 Tabular (formerly "subsettable") data files have DataTable objects | |
| 69 associated with them: | |
| 70 */ | |
| 71 | |
| 72 @OneToMany(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 73 private List<DataTable> dataTables; | |
| 74 | |
| 75 @OneToMany(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 76 private List<IngestReport> ingestReports; | |
| 77 | |
| 78 @OneToOne(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 79 private IngestRequest ingestRequest; | |
| 80 | |
| 81 @OneToMany(mappedBy = "dataFile", orphanRemoval = true, cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 82 private List<DataFileTag> dataFileTags; | |
| 83 | |
| 84 @OneToMany(mappedBy="dataFile", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 85 private List<FileMetadata> fileMetadatas; | |
| 86 | |
| 87 @OneToMany(mappedBy="dataFile", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | |
| 88 private List<GuestbookResponse> guestbookResponses; | |
| 89 | |
| 90 public List<GuestbookResponse> getGuestbookResponses() { | |
| 91 return guestbookResponses; | |
| 92 } | |
| 93 | |
| 94 public void setGuestbookResponses(List<GuestbookResponse> guestbookResponses) { | |
| 95 this.guestbookResponses = guestbookResponses; | |
| 96 } | |
| 97 | |
| 98 private char ingestStatus = INGEST_STATUS_NONE; | |
| 99 | |
| 100 @OneToOne(mappedBy = "thumbnailFile") | |
| 101 private Dataset thumbnailForDataset; | |
| 102 | |
| 103 | |
| 104 public DataFile() { | |
| 105 this.fileMetadatas = new ArrayList<>(); | |
| 106 } | |
| 107 | |
| 108 public DataFile(String contentType) { | |
| 109 this.contentType = contentType; | |
| 110 this.fileMetadatas = new ArrayList<>(); | |
| 111 } | |
| 112 | |
| 113 // The dvObject field "name" should not be used in | |
| 114 // datafile objects. | |
| 115 // The file name must be stored in the file metadata. | |
| 116 @Deprecated | |
| 117 public DataFile(String name, String contentType) { | |
| 118 this.name = name; | |
| 119 this.contentType = contentType; | |
| 120 this.fileMetadatas = new ArrayList<>(); | |
| 121 } | |
| 122 | |
| 123 @Override | |
| 124 public boolean isEffectivelyPermissionRoot() { | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 128 public List<DataTable> getDataTables() { | |
| 129 return dataTables; | |
| 130 } | |
| 131 | |
| 132 public void setDataTables(List<DataTable> dataTables) { | |
| 133 this.dataTables = dataTables; | |
| 134 } | |
| 135 | |
| 136 public DataTable getDataTable() { | |
| 137 if ( getDataTables() != null && getDataTables().size() > 0 ) { | |
| 138 return getDataTables().get(0); | |
| 139 } else { | |
| 140 return null; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 public void setDataTable(DataTable dt) { | |
| 145 if (this.getDataTables() == null) { | |
| 146 this.setDataTables( new ArrayList() ); | |
| 147 } else { | |
| 148 this.getDataTables().clear(); | |
| 149 } | |
| 150 | |
| 151 this.getDataTables().add(dt); | |
| 152 } | |
| 153 | |
| 154 public List<DataFileTag> getTags() { | |
| 155 return dataFileTags; | |
| 156 } | |
| 157 | |
| 158 public void setTags(List<DataFileTag> dataFileTags) { | |
| 159 this.dataFileTags = dataFileTags; | |
| 160 } | |
| 161 | |
| 162 public void addTag(DataFileTag tag) { | |
| 163 if (dataFileTags == null) { | |
| 164 dataFileTags = new ArrayList<>(); | |
| 165 } | |
| 166 | |
| 167 dataFileTags.add(tag); | |
| 168 } | |
| 169 | |
| 170 public List<FileMetadata> getFileMetadatas() { | |
| 171 return fileMetadatas; | |
| 172 } | |
| 173 | |
| 174 public void setFileMetadatas(List<FileMetadata> fileMetadatas) { | |
| 175 this.fileMetadatas = fileMetadatas; | |
| 176 } | |
| 177 | |
| 178 public IngestReport getIngestReport() { | |
| 179 if ( ingestReports != null && ingestReports.size() > 0 ) { | |
| 180 return ingestReports.get(0); | |
| 181 } else { | |
| 182 return null; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 public void setIngestReport(IngestReport report) { | |
| 187 if (ingestReports == null) { | |
| 188 ingestReports = new ArrayList(); | |
| 189 } else { | |
| 190 ingestReports.clear(); | |
| 191 } | |
| 192 | |
| 193 ingestReports.add(report); | |
| 194 } | |
| 195 | |
| 196 public IngestRequest getIngestRequest() { | |
| 197 return ingestRequest; | |
| 198 } | |
| 199 | |
| 200 public void setIngestRequest(IngestRequest ingestRequest) { | |
| 201 this.ingestRequest = ingestRequest; | |
| 202 } | |
| 203 | |
| 204 public String getIngestReportMessage() { | |
| 205 if ( ingestReports != null && ingestReports.size() > 0 ) { | |
| 206 if (ingestReports.get(0).getReport() != null && !"".equals(ingestReports.get(0).getReport())) { | |
| 207 return ingestReports.get(0).getReport(); | |
| 208 } | |
| 209 } | |
| 210 return "Ingest failed. No further information is available."; | |
| 211 } | |
| 212 public boolean isTabularData() { | |
| 213 return getDataTables() != null && getDataTables().size() > 0; | |
| 214 } | |
| 215 | |
| 216 public String getOriginalFileFormat() { | |
| 217 if (isTabularData()) { | |
| 218 DataTable dataTable = getDataTable(); | |
| 219 if (dataTable != null) { | |
| 220 return dataTable.getOriginalFileFormat(); | |
| 221 } | |
| 222 } | |
| 223 return null; | |
| 224 } | |
| 225 | |
| 226 /* | |
| 227 * A user-friendly version of the "original format": | |
| 228 */ | |
| 229 public String getOriginalFormatLabel() { | |
| 230 return FileUtil.getUserFriendlyOriginalType(this); | |
| 231 } | |
| 232 | |
| 233 // The dvObject field "name" should not be used in | |
| 234 // datafile objects. | |
| 235 // The file name must be stored in the file metadata. | |
| 236 @Deprecated | |
| 237 public String getName() { | |
| 238 return name; | |
| 239 } | |
| 240 | |
| 241 @Deprecated | |
| 242 public void setName(String name) { | |
| 243 this.name = name; | |
| 244 } | |
| 245 | |
| 246 public String getContentType() { | |
| 247 return contentType; | |
| 248 } | |
| 249 | |
| 250 public void setContentType(String contentType) { | |
| 251 this.contentType = contentType; | |
| 252 } | |
| 253 | |
| 254 public String getFriendlyType() { | |
| 255 return FileUtil.getUserFriendlyFileType(this); | |
| 256 } | |
| 257 | |
| 258 @Override | |
| 259 public Dataset getOwner() { | |
| 260 return (Dataset) super.getOwner(); | |
| 261 } | |
| 262 | |
| 263 public void setOwner(Dataset dataset) { | |
| 264 super.setOwner(dataset); | |
| 265 } | |
| 266 | |
| 267 public String getFileSystemName() { | |
| 268 return this.fileSystemName; | |
| 269 } | |
| 270 | |
| 271 public void setFileSystemName(String fileSystemName) { | |
| 272 this.fileSystemName = fileSystemName; | |
| 273 } | |
| 274 | |
| 275 public String getDescription() { | |
| 276 FileMetadata fmd = getLatestFileMetadata(); | |
| 277 | |
| 278 if (fmd == null) { | |
| 279 return null; | |
| 280 } | |
| 281 return fmd.getDescription(); | |
| 282 } | |
| 283 | |
| 284 public void setDescription(String description) { | |
| 285 FileMetadata fmd = getLatestFileMetadata(); | |
| 286 | |
| 287 if (fmd != null) { | |
| 288 fmd.setDescription(description); | |
| 289 } | |
| 290 } | |
| 291 | |
| 292 public FileMetadata getFileMetadata() { | |
| 293 return getLatestFileMetadata(); | |
| 294 } | |
| 295 | |
| 296 private FileMetadata getLatestFileMetadata() { | |
| 297 FileMetadata fmd = null; | |
| 298 | |
| 299 // for newly added or harvested, just return the one fmd | |
| 300 if (fileMetadatas.size() == 1) { | |
| 301 return fileMetadatas.get(0); | |
| 302 } | |
| 303 | |
| 304 for (FileMetadata fileMetadata : fileMetadatas) { | |
| 305 // if it finds a draft, return it | |
| 306 if (fileMetadata.getDatasetVersion().getVersionState().equals(VersionState.DRAFT)) { | |
| 307 return fileMetadata; | |
| 308 } | |
| 309 | |
| 310 // otherwise return the one with the latest version number | |
| 311 if (fmd == null || fileMetadata.getDatasetVersion().getVersionNumber().compareTo( fmd.getDatasetVersion().getVersionNumber() ) > 0 ) { | |
| 312 fmd = fileMetadata; | |
| 313 } else if ((fileMetadata.getDatasetVersion().getVersionNumber().compareTo( fmd.getDatasetVersion().getVersionNumber())==0 )&& | |
| 314 ( fileMetadata.getDatasetVersion().getMinorVersionNumber().compareTo( fmd.getDatasetVersion().getMinorVersionNumber()) > 0 ) ) | |
| 315 fmd = fileMetadata; | |
| 316 } | |
| 317 return fmd; | |
| 318 } | |
| 319 | |
| 320 /** | |
| 321 * Get property filesize, number of bytes | |
| 322 * @return value of property filesize. | |
| 323 */ | |
| 324 public long getFilesize() { | |
| 325 if (this.filesize == null) { | |
| 326 // -1 means "unknown" | |
| 327 return -1; | |
| 328 } | |
| 329 return this.filesize; | |
| 330 } | |
| 331 | |
| 332 /** | |
| 333 * Set property filesize in bytes | |
| 334 * | |
| 335 * Allow nulls, but not negative numbers. | |
| 336 * | |
| 337 * @param filesize new value of property filesize. | |
| 338 */ | |
| 339 public void setFilesize(long filesize) { | |
| 340 if (filesize < 0){ | |
| 341 return; | |
| 342 } | |
| 343 this.filesize = filesize; | |
| 344 } | |
| 345 | |
| 346 /** | |
| 347 * Converts the stored size of the file in bytes to | |
| 348 * a user-friendly value in KB, MB or GB. | |
| 349 */ | |
| 350 public String getFriendlySize() { | |
| 351 return FileUtil.getFriendlySize(filesize); | |
| 352 } | |
| 353 | |
| 354 public boolean isRestricted() { | |
| 355 return restricted; | |
| 356 } | |
| 357 | |
| 358 public void setRestricted(boolean restricted) { | |
| 359 this.restricted = restricted; | |
| 360 } | |
| 361 | |
| 362 | |
| 363 public String getmd5() { | |
| 364 return this.md5; | |
| 365 } | |
| 366 | |
| 367 public void setmd5(String md5) { | |
| 368 this.md5 = md5; | |
| 369 } | |
| 370 | |
| 371 public Path getFileSystemLocation() { | |
| 372 // TEMPORARY HACK! | |
| 373 // (only used in batch ingest testing -- L.A. 4.0 beta) | |
| 374 if (this.fileSystemName != null && this.fileSystemName.startsWith("/")) { | |
| 375 return Paths.get(this.fileSystemName); | |
| 376 } | |
| 377 | |
| 378 Path studyDirectoryPath = this.getOwner().getFileSystemDirectory(); | |
| 379 if (studyDirectoryPath == null) { | |
| 380 return null; | |
| 381 } | |
| 382 String studyDirectory = studyDirectoryPath.toString(); | |
| 383 | |
| 384 return Paths.get(studyDirectory, this.fileSystemName); | |
| 385 } | |
| 386 | |
| 387 public DataAccessObject getAccessObject() throws IOException { | |
| 388 DataAccessObject dataAccess = DataAccess.createDataAccessObject(this); | |
| 389 | |
| 390 if (dataAccess == null) { | |
| 391 throw new IOException("Failed to create access object for datafile."); | |
| 392 } | |
| 393 | |
| 394 return dataAccess; | |
| 395 } | |
| 396 | |
| 397 public Path getSavedOriginalFile() { | |
| 398 | |
| 399 if (!this.isTabularData() || this.fileSystemName == null) { | |
| 400 return null; | |
| 401 } | |
| 402 | |
| 403 Path studyDirectoryPath = this.getOwner().getFileSystemDirectory(); | |
| 404 if (studyDirectoryPath == null) { | |
| 405 return null; | |
| 406 } | |
| 407 String studyDirectory = studyDirectoryPath.toString(); | |
| 408 | |
| 409 Path savedOriginal = Paths.get(studyDirectory, "_" + this.fileSystemName); | |
| 410 if (Files.exists(savedOriginal)) { | |
| 411 return savedOriginal; | |
| 412 } | |
| 413 return null; | |
| 414 } | |
| 415 | |
| 416 public String getFilename() { | |
| 417 String studyDirectory = this.getOwner().getFileSystemDirectory().toString(); | |
| 418 | |
| 419 if (studyDirectory == null || this.fileSystemName == null || this.fileSystemName.equals("")) { | |
| 420 return null; | |
| 421 } | |
| 422 String fileSystemPath = studyDirectory + "/" + this.fileSystemName; | |
| 423 return fileSystemPath.replaceAll("/", "%2F"); | |
| 424 } | |
| 425 | |
| 426 /* | |
| 427 Does the contentType indicate a shapefile? | |
| 428 */ | |
| 429 public boolean isShapefileType(){ | |
| 430 if (this.contentType==null){ | |
| 431 return false; | |
| 432 } | |
| 433 return ShapefileHandler.SHAPEFILE_FILE_TYPE.equalsIgnoreCase(this.contentType); | |
| 434 } | |
| 435 | |
| 436 public boolean isImage() { | |
| 437 // Some browsers (Chrome?) seem to identify FITS files as mime | |
| 438 // type "image/fits" on upload; this is both incorrect (the official | |
| 439 // mime type for FITS is "application/fits", and problematic: then | |
| 440 // the file is identified as an image, and the page will attempt to | |
| 441 // generate a preview - which of course is going to fail... | |
| 442 if ("image/fits".equalsIgnoreCase(contentType)) { | |
| 443 return false; | |
| 444 } | |
| 445 // a pdf file is an "image" for practical purposes (we will attempt to | |
| 446 // generate thumbnails and previews for them) | |
| 447 return (contentType != null && (contentType.startsWith("image/") || contentType.equalsIgnoreCase("application/pdf"))); | |
| 448 } | |
| 449 | |
| 450 public boolean isIngestScheduled() { | |
| 451 return (ingestStatus == INGEST_STATUS_SCHEDULED); | |
| 452 } | |
| 453 | |
| 454 public boolean isIngestInProgress() { | |
| 455 return ((ingestStatus == INGEST_STATUS_SCHEDULED) || (ingestStatus == INGEST_STATUS_INPROGRESS)); | |
| 456 } | |
| 457 | |
| 458 public boolean isIngestProblem() { | |
| 459 return (ingestStatus == INGEST_STATUS_ERROR); | |
| 460 } | |
| 461 | |
| 462 public void SetIngestScheduled() { | |
| 463 ingestStatus = INGEST_STATUS_SCHEDULED; | |
| 464 } | |
| 465 | |
| 466 public void SetIngestInProgress() { | |
| 467 ingestStatus = INGEST_STATUS_INPROGRESS; | |
| 468 } | |
| 469 | |
| 470 public void SetIngestProblem() { | |
| 471 ingestStatus = INGEST_STATUS_ERROR; | |
| 472 } | |
| 473 | |
| 474 public void setIngestDone() { | |
| 475 ingestStatus = INGEST_STATUS_NONE; | |
| 476 } | |
| 477 | |
| 478 public int getIngestStatus() { | |
| 479 return ingestStatus; | |
| 480 } | |
| 481 | |
| 482 public Dataset getThumbnailForDataset() { | |
| 483 return thumbnailForDataset; | |
| 484 } | |
| 485 | |
| 486 public void setAsThumbnailForDataset(Dataset dataset) { | |
| 487 thumbnailForDataset = dataset; | |
| 488 } | |
| 489 | |
| 490 /** | |
| 491 * URL to use with the WorldMapRelatedData API | |
| 492 * Used within dataset.xhtml | |
| 493 * | |
| 494 * @param dataverseUserID | |
| 495 * @return URL for "Map It" functionality | |
| 496 */ | |
| 497 public String getMapItURL(Long dataverseUserID){ | |
| 498 if (dataverseUserID==null){ | |
| 499 return null; | |
| 500 } | |
| 501 return WorldMapRelatedData.getMapItURL(this.getId(), dataverseUserID); | |
| 502 } | |
| 503 | |
| 504 /* | |
| 505 8/10/2014 - Using the current "open access" url | |
| 506 */ | |
| 507 public String getMapItFileDownloadURL(String serverName){ | |
| 508 if ((this.getId() == null)||(serverName == null)){ | |
| 509 return null; | |
| 510 } | |
| 511 return serverName + "/api/access/datafile/" + this.getId(); | |
| 512 } | |
| 513 | |
| 514 /* | |
| 515 * If this is tabular data, the corresponding dataTable may have a UNF - | |
| 516 * "numeric fingerprint" signature - generated: | |
| 517 */ | |
| 518 | |
| 519 public String getUnf() { | |
| 520 if (this.isTabularData()) { | |
| 521 // (isTabularData() method above verifies that that this file | |
| 522 // has a datDatable associated with it, so the line below is | |
| 523 // safe, in terms of a NullPointerException: | |
| 524 return this.getDataTable().getUnf(); | |
| 525 } | |
| 526 return null; | |
| 527 } | |
| 528 | |
| 529 | |
| 530 @ManyToMany | |
| 531 @JoinTable(name = "fileaccessrequests", | |
| 532 joinColumns = @JoinColumn(name = "datafile_id"), | |
| 533 inverseJoinColumns = @JoinColumn(name = "authenticated_user_id")) | |
| 534 private List<AuthenticatedUser> fileAccessRequesters; | |
| 535 | |
| 536 public List<AuthenticatedUser> getFileAccessRequesters() { | |
| 537 return fileAccessRequesters; | |
| 538 } | |
| 539 | |
| 540 public void setFileAccessRequesters(List<AuthenticatedUser> fileAccessRequesters) { | |
| 541 this.fileAccessRequesters = fileAccessRequesters; | |
| 542 } | |
| 543 | |
| 544 | |
| 545 public boolean isHarvested() { | |
| 546 // TODO: | |
| 547 // alternatively, we can determine whether this is a harvested file | |
| 548 // by looking at the storage identifier of the physical file; | |
| 549 // if it's something that's not a filesystem path (URL, etc.) - | |
| 550 // then it's a harvested object. | |
| 551 // -- L.A. 4.0 | |
| 552 Dataset ownerDataset = this.getOwner(); | |
| 553 if (ownerDataset != null) { | |
| 554 return ownerDataset.isHarvested(); | |
| 555 } | |
| 556 return false; | |
| 557 } | |
| 558 | |
| 559 public String getRemoteArchiveURL() { | |
| 560 if (isHarvested()) { | |
| 561 Dataset ownerDataset = this.getOwner(); | |
| 562 return ownerDataset.getRemoteArchiveURL(); | |
| 563 } | |
| 564 | |
| 565 return null; | |
| 566 } | |
| 567 | |
| 568 public String getHarvestingDescription() { | |
| 569 if (isHarvested()) { | |
| 570 Dataset ownerDataset = this.getOwner(); | |
| 571 return ownerDataset.getHarvestingDescription(); | |
| 572 } | |
| 573 | |
| 574 return null; | |
| 575 } | |
| 576 | |
| 577 @Override | |
| 578 public boolean equals(Object object) { | |
| 579 if (!(object instanceof DataFile)) { | |
| 580 return false; | |
| 581 } | |
| 582 DataFile other = (DataFile) object; | |
| 583 return Objects.equals(getId(), other.getId()); | |
| 584 } | |
| 585 | |
| 586 @Override | |
| 587 public int hashCode() { | |
| 588 return super.hashCode(); | |
| 589 } | |
| 590 | |
| 591 @Override | |
| 592 protected String toStringExtras() { | |
| 593 return "name:" + getName(); | |
| 594 } | |
| 595 | |
| 596 @Override | |
| 597 public <T> T accept( Visitor<T> v ) { | |
| 598 return v.visit(this); | |
| 599 } | |
| 600 | |
| 601 public String getDisplayName() { | |
| 602 // @todo should we show the published version label instead? | |
| 603 // currently this method is not being used | |
| 604 return getLatestFileMetadata().getLabel(); | |
| 605 } | |
| 606 } |
