001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file; 029 030import org.opencms.db.CmsDriverManager; 031import org.opencms.db.CmsResourceState; 032import org.opencms.file.types.I_CmsResourceType; 033import org.opencms.main.CmsIllegalArgumentException; 034import org.opencms.util.A_CmsModeIntEnumeration; 035import org.opencms.util.CmsStringUtil; 036import org.opencms.util.CmsUUID; 037 038import java.io.Serializable; 039 040/** 041 * Base class for all OpenCms VFS resources like <code>{@link CmsFile}</code> or <code>{@link CmsFolder}</code>.<p> 042 * 043 * The OpenCms VFS resource is an important object for using the OpenCms API. 044 * Basically, all entries in the OpenCms VFS are considered to be "resources". 045 * Currently, only two types of resources exists:<ul> 046 * <li>Files, which are represented by the subclass {@link CmsFile}. 047 * <li>Folders (also called Directories), which are represented by the subclass {@link CmsFolder}. 048 * </ul> 049 * 050 * If you have a resource, you can use {@link #isFile()} or {@link #isFolder()} to learn what kind of 051 * subclass you have. Please note that this is usually not required, as the only real difference between a 052 * {@link CmsFile} and a {@link CmsResource} is that the {@link CmsFile} also has the contents of the file, 053 * which you can obtain using {@link CmsFile#getContents()}. As long as you don't need the content, you can 054 * use the {@link CmsResource} for everything else. This is even more true for a {@link CmsFolder}, here you 055 * will need the subclass only in special cases, since the signature is identical to {@link CmsResource}.<p> 056 * 057 * A OpenCms VFS resource can have any number of properties attached, which are represented by a {@link CmsProperty}. 058 * To read the properties for a resource, use {@link CmsObject#readPropertyObject(CmsResource, String, boolean)} 059 * or use {@link CmsObject#readPropertyObjects(CmsResource, boolean)} to read all properties of the resource.<p> 060 * 061 * @since 6.0.0 062 */ 063public class CmsResource implements I_CmsResource, Cloneable, Serializable, Comparable<I_CmsResource> { 064 065 /** 066 * Enumeration class for resource copy modes.<p> 067 */ 068 public static final class CmsResourceCopyMode extends A_CmsModeIntEnumeration { 069 070 /** Copy mode for copy resources as new resource. */ 071 protected static final CmsResourceCopyMode MODE_COPY_AS_NEW = new CmsResourceCopyMode(1); 072 073 /** Copy mode for copy resources as sibling. */ 074 protected static final CmsResourceCopyMode MODE_COPY_AS_SIBLING = new CmsResourceCopyMode(2); 075 076 /** Copy mode to preserve siblings during copy. */ 077 protected static final CmsResourceCopyMode MODE_COPY_PRESERVE_SIBLING = new CmsResourceCopyMode(3); 078 079 /** Version id required for safe serialization. */ 080 private static final long serialVersionUID = 9081630878178799137L; 081 082 /** 083 * Private constructor.<p> 084 * 085 * @param mode the copy mode integer representation 086 */ 087 private CmsResourceCopyMode(int mode) { 088 089 super(mode); 090 } 091 092 /** 093 * Returns the copy mode object from the old copy mode integer.<p> 094 * 095 * @param mode the old copy mode integer 096 * 097 * @return the copy mode object 098 */ 099 public static CmsResourceCopyMode valueOf(int mode) { 100 101 switch (mode) { 102 case 1: 103 return CmsResourceCopyMode.MODE_COPY_AS_NEW; 104 case 2: 105 return CmsResourceCopyMode.MODE_COPY_AS_SIBLING; 106 case 3: 107 default: 108 return CmsResourceCopyMode.MODE_COPY_PRESERVE_SIBLING; 109 } 110 } 111 } 112 113 /** 114 * Enumeration class for resource delete modes.<p> 115 */ 116 public static final class CmsResourceDeleteMode extends A_CmsModeIntEnumeration { 117 118 /** Signals that siblings of this resource should not be deleted. */ 119 protected static final CmsResourceDeleteMode MODE_DELETE_PRESERVE_SIBLINGS = new CmsResourceDeleteMode(1); 120 121 /** Signals that siblings of this resource should be deleted. */ 122 protected static final CmsResourceDeleteMode MODE_DELETE_REMOVE_SIBLINGS = new CmsResourceDeleteMode(2); 123 124 /** Version id required for safe serialization. */ 125 private static final long serialVersionUID = 2010402524576925865L; 126 127 /** 128 * Private constructor.<p> 129 * 130 * @param mode the delete mode integer representation 131 */ 132 private CmsResourceDeleteMode(int mode) { 133 134 super(mode); 135 } 136 137 /** 138 * Returns the delete mode object from the old delete mode integer.<p> 139 * 140 * @param mode the old delete mode integer 141 * 142 * @return the delete mode object 143 */ 144 public static CmsResourceDeleteMode valueOf(int mode) { 145 146 switch (mode) { 147 case 1: 148 return CmsResourceDeleteMode.MODE_DELETE_PRESERVE_SIBLINGS; 149 case 2: 150 default: 151 return CmsResourceDeleteMode.MODE_DELETE_REMOVE_SIBLINGS; 152 } 153 } 154 } 155 156 /** 157 * Enumeration class for resource undo changes modes.<p> 158 */ 159 public static final class CmsResourceUndoMode extends A_CmsModeIntEnumeration { 160 161 /** Indicates that the undo method will only undo content changes. */ 162 public static final CmsResourceUndoMode MODE_UNDO_CONTENT = new CmsResourceUndoMode(1); 163 164 /** Indicates that the undo method will only recursive undo content changes. */ 165 public static final CmsResourceUndoMode MODE_UNDO_CONTENT_RECURSIVE = new CmsResourceUndoMode(2); 166 167 /** Indicates that the undo method will undo move operations and content changes. */ 168 public static final CmsResourceUndoMode MODE_UNDO_MOVE_CONTENT = new CmsResourceUndoMode(3); 169 170 /** Indicates that the undo method will undo move operations and recursive content changes. */ 171 public static final CmsResourceUndoMode MODE_UNDO_MOVE_CONTENT_RECURSIVE = new CmsResourceUndoMode(4); 172 173 /** Version id required for safe serialization. */ 174 private static final long serialVersionUID = 3521620626485212068L; 175 176 /** 177 * private constructor.<p> 178 * 179 * @param mode the undo changes mode integer representation 180 */ 181 private CmsResourceUndoMode(int mode) { 182 183 super(mode); 184 } 185 186 /** 187 * Gets the undo mode for the given parameters.<p> 188 * 189 * @param move flag for undoing move operations 190 * @param recursive flag for recursive undo 191 * 192 * @return the undo mode 193 */ 194 public static CmsResourceUndoMode getUndoMode(boolean move, boolean recursive) { 195 196 if (move) { 197 return recursive 198 ? CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT_RECURSIVE 199 : CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT; 200 } else { 201 return recursive 202 ? CmsResourceUndoMode.MODE_UNDO_CONTENT_RECURSIVE 203 : CmsResourceUndoMode.MODE_UNDO_CONTENT; 204 } 205 } 206 207 /** 208 * Returns the undo mode object from the old undo mode integer.<p> 209 * 210 * @param mode the old undo mode integer 211 * 212 * @return the undo mode object 213 */ 214 public static CmsResourceUndoMode valueOf(int mode) { 215 216 switch (mode) { 217 case 1: 218 return CmsResourceUndoMode.MODE_UNDO_CONTENT; 219 case 2: 220 return CmsResourceUndoMode.MODE_UNDO_CONTENT_RECURSIVE; 221 case 3: 222 return CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT; 223 case 4: 224 default: 225 return CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT_RECURSIVE; 226 } 227 } 228 229 /** 230 * Returns a mode that includes the move operation with the same semantic as this mode.<p> 231 * 232 * @return a mode that includes the move operation with the same semantic as this mode 233 */ 234 public CmsResourceUndoMode includeMove() { 235 236 if (!isUndoMove()) { 237 // keep the same semantic but including move 238 return CmsResourceUndoMode.valueOf(getMode() + 2); 239 } 240 return this; 241 } 242 243 /** 244 * Returns <code>true</code> if this undo operation is recursive.<p> 245 * 246 * @return <code>true</code> if this undo operation is recursive 247 */ 248 public boolean isRecursive() { 249 250 return getMode() > CmsResource.UNDO_CONTENT.getMode(); 251 } 252 253 /** 254 * Returns <code>true</code> if this undo mode will undo move operations.<p> 255 * 256 * @return <code>true</code> if this undo mode will undo move operations 257 */ 258 public boolean isUndoMove() { 259 260 return getMode() > CmsResource.UNDO_CONTENT_RECURSIVE.getMode(); 261 } 262 263 /** 264 * @see java.lang.Object#toString() 265 */ 266 @Override 267 public String toString() { 268 269 return String.valueOf(getMode()); 270 } 271 } 272 273 /** Copy mode for copy resources as new resource. */ 274 public static final CmsResourceCopyMode COPY_AS_NEW = CmsResourceCopyMode.MODE_COPY_AS_NEW; 275 276 /** Copy mode for copy resources as sibling. */ 277 public static final CmsResourceCopyMode COPY_AS_SIBLING = CmsResourceCopyMode.MODE_COPY_AS_SIBLING; 278 279 /** Copy mode to preserve siblings during copy. */ 280 public static final CmsResourceCopyMode COPY_PRESERVE_SIBLING = CmsResourceCopyMode.MODE_COPY_PRESERVE_SIBLING; 281 282 /** The default expiration date of a resource, which is: never expires. */ 283 public static final long DATE_EXPIRED_DEFAULT = Long.MAX_VALUE; 284 285 /** The default release date of a resource, which is: always released. */ 286 public static final long DATE_RELEASED_DEFAULT = 0; 287 288 /** A special date that indicates release and expiration information are to be ignored. */ 289 public static final long DATE_RELEASED_EXPIRED_IGNORE = Long.MIN_VALUE; 290 291 /** Signals that siblings of this resource should not be deleted. */ 292 public static final CmsResourceDeleteMode DELETE_PRESERVE_SIBLINGS = CmsResourceDeleteMode.MODE_DELETE_PRESERVE_SIBLINGS; 293 294 /** Signals that siblings of this resource should be deleted. */ 295 public static final CmsResourceDeleteMode DELETE_REMOVE_SIBLINGS = CmsResourceDeleteMode.MODE_DELETE_REMOVE_SIBLINGS; 296 297 /** Flag to indicate that this is an internal resource, that can't be accessed directly. */ 298 public static final int FLAG_INTERNAL = 512; 299 300 /** The resource is linked inside a site folder specified in the OpenCms configuration. */ 301 public static final int FLAG_LABELED = 2; 302 303 /** Flag to indicate that this is a temporary resource. */ 304 public static final int FLAG_TEMPFILE = 1024; 305 306 /** The name constraints when generating new resources. */ 307 public static final String NAME_CONSTRAINTS = "-._~$"; 308 309 /** Indicates if a resource has been changed in the offline version when compared to the online version. */ 310 public static final CmsResourceState STATE_CHANGED = CmsResourceState.STATE_CHANGED; 311 312 /** Indicates if a resource has been deleted in the offline version when compared to the online version. */ 313 public static final CmsResourceState STATE_DELETED = CmsResourceState.STATE_DELETED; 314 315 /** 316 * Special state value that indicates the current state must be kept on a resource, 317 * this value must never be written to the database. 318 */ 319 public static final CmsResourceState STATE_KEEP = CmsResourceState.STATE_KEEP; 320 321 /** Indicates if a resource is new in the offline version when compared to the online version. */ 322 public static final CmsResourceState STATE_NEW = CmsResourceState.STATE_NEW; 323 324 /** Indicates if a resource is unchanged in the offline version when compared to the online version. */ 325 public static final CmsResourceState STATE_UNCHANGED = CmsResourceState.STATE_UNCHANGED; 326 327 /** 328 * Prefix for temporary files in the VFS. 329 * 330 * @see #isTemporaryFile() 331 * @see #isTemporaryFileName(String) 332 */ 333 public static final String TEMP_FILE_PREFIX = CmsDriverManager.TEMP_FILE_PREFIX; 334 335 /** Flag for leaving a date unchanged during a touch operation. */ 336 public static final long TOUCH_DATE_UNCHANGED = -1; 337 338 /** Indicates that the undo method will only undo content changes. */ 339 public static final CmsResourceUndoMode UNDO_CONTENT = CmsResourceUndoMode.MODE_UNDO_CONTENT; 340 341 /** Indicates that the undo method will only recursive undo content changes. */ 342 public static final CmsResourceUndoMode UNDO_CONTENT_RECURSIVE = CmsResourceUndoMode.MODE_UNDO_CONTENT_RECURSIVE; 343 344 /** Indicates that the undo method will undo move operations and content changes. */ 345 public static final CmsResourceUndoMode UNDO_MOVE_CONTENT = CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT; 346 347 /** Indicates that the undo method will undo move operations and recursive content changes. */ 348 public static final CmsResourceUndoMode UNDO_MOVE_CONTENT_RECURSIVE = CmsResourceUndoMode.MODE_UNDO_MOVE_CONTENT_RECURSIVE; 349 350 /** The vfs path of the sites master folder. */ 351 public static final String VFS_FOLDER_SITES = "/sites"; 352 353 /** The vfs path of the system folder. */ 354 public static final String VFS_FOLDER_SYSTEM = "/system"; 355 356 /** Serial version UID required for safe serialization. */ 357 private static final long serialVersionUID = 257325098790850498L; 358 359 /** The date of the last modification of the content of this resource. */ 360 protected long m_dateContent = System.currentTimeMillis(); 361 362 /** The size of the content. */ 363 protected int m_length; 364 365 /** The creation date of this resource. */ 366 private long m_dateCreated; 367 368 /** The expiration date of this resource. */ 369 private long m_dateExpired; 370 371 /** The date of the last modification of this resource. */ 372 private long m_dateLastModified; 373 374 /** The release date of this resource. */ 375 private long m_dateReleased; 376 377 /** The flags of this resource. */ 378 private int m_flags; 379 380 /** Indicates if this resource is a folder or not. */ 381 private boolean m_isFolder; 382 383 /** Boolean flag whether the timestamp of this resource was modified by a touch command. */ 384 private boolean m_isTouched; 385 386 /** The project id where this resource has been last modified in. */ 387 private CmsUUID m_projectLastModified; 388 389 /** The id of the resource database record. */ 390 private CmsUUID m_resourceId; 391 392 /** The name of a resource with it's full path from the root folder including the current site root. */ 393 private String m_rootPath; 394 395 /** The number of links that point to this resource. */ 396 private int m_siblingCount; 397 398 /** The state of this resource. */ 399 private CmsResourceState m_state; 400 401 /** The id of the structure database record. */ 402 private CmsUUID m_structureId; 403 404 /** The resource type id of this resource. */ 405 private int m_typeId; 406 407 /** The id of the user who created this resource. */ 408 private CmsUUID m_userCreated; 409 410 /** The id of the user who modified this resource last. */ 411 private CmsUUID m_userLastModified; 412 413 /** The version number of this resource. */ 414 private int m_version; 415 416 /** 417 * Creates a new CmsRecource object.<p> 418 * 419 * @param structureId the id of this resources structure record 420 * @param resourceId the id of this resources resource record 421 * @param rootPath the root path to the resource 422 * @param type the type of this resource 423 * @param flags the flags of this resource 424 * @param projectId the project id this resource was last modified in 425 * @param state the state of this resource 426 * @param dateCreated the creation date of this resource 427 * @param userCreated the id of the user who created this resource 428 * @param dateLastModified the date of the last modification of this resource 429 * @param userLastModified the id of the user who did the last modification of this resource 430 * @param dateReleased the release date of this resource 431 * @param dateExpired the expiration date of this resource 432 * @param linkCount the count of all siblings of this resource 433 * @param size the size of the file content of this resource 434 * @param dateContent the date of the last modification of the content of this resource 435 * @param version the version number of this resource 436 */ 437 public CmsResource( 438 CmsUUID structureId, 439 CmsUUID resourceId, 440 String rootPath, 441 I_CmsResourceType type, 442 int flags, 443 CmsUUID projectId, 444 CmsResourceState state, 445 long dateCreated, 446 CmsUUID userCreated, 447 long dateLastModified, 448 CmsUUID userLastModified, 449 long dateReleased, 450 long dateExpired, 451 int linkCount, 452 int size, 453 long dateContent, 454 int version) { 455 456 this( 457 structureId, 458 resourceId, 459 rootPath, 460 type.getTypeId(), 461 type.isFolder(), 462 flags, 463 projectId, 464 state, 465 dateCreated, 466 userCreated, 467 dateLastModified, 468 userLastModified, 469 dateReleased, 470 dateExpired, 471 linkCount, 472 size, 473 dateContent, 474 version); 475 } 476 477 /** 478 * Creates a new CmsRecource object.<p> 479 * 480 * @param structureId the id of this resources structure record 481 * @param resourceId the id of this resources resource record 482 * @param rootPath the root path to the resource 483 * @param type the type of this resource 484 * @param isFolder must be true if the resource is a folder, or false if it is a file 485 * @param flags the flags of this resource 486 * @param projectId the project id this resource was last modified in 487 * @param state the state of this resource 488 * @param dateCreated the creation date of this resource 489 * @param userCreated the id of the user who created this resource 490 * @param dateLastModified the date of the last modification of this resource 491 * @param userLastModified the id of the user who did the last modification of this resource 492 * @param dateReleased the release date of this resource 493 * @param dateExpired the expiration date of this resource 494 * @param linkCount the count of all siblings of this resource 495 * @param size the size of the file content of this resource 496 * @param dateContent the date of the last modification of the content of this resource 497 * @param version the version number of this resource 498 */ 499 public CmsResource( 500 CmsUUID structureId, 501 CmsUUID resourceId, 502 String rootPath, 503 int type, 504 boolean isFolder, 505 int flags, 506 CmsUUID projectId, 507 CmsResourceState state, 508 long dateCreated, 509 CmsUUID userCreated, 510 long dateLastModified, 511 CmsUUID userLastModified, 512 long dateReleased, 513 long dateExpired, 514 int linkCount, 515 int size, 516 long dateContent, 517 int version) { 518 519 m_structureId = structureId; 520 m_resourceId = resourceId; 521 m_rootPath = rootPath; 522 m_typeId = type; 523 m_isFolder = isFolder; 524 m_flags = flags; 525 m_projectLastModified = projectId; 526 m_state = state; 527 m_dateCreated = dateCreated; 528 m_userCreated = userCreated; 529 m_dateLastModified = dateLastModified; 530 m_userLastModified = userLastModified; 531 m_dateReleased = dateReleased; 532 m_dateExpired = dateExpired; 533 m_siblingCount = linkCount; 534 m_length = size; 535 m_dateContent = dateContent; 536 m_version = version; 537 m_isTouched = false; 538 } 539 540 /** 541 * Checks if the provided resource name is a valid resource name, 542 * that is contains only valid characters.<p> 543 * 544 * A resource name can only be composed of digits, 545 * standard ASCII letters and the symbols defined in {@link #NAME_CONSTRAINTS}. 546 * A resource name must also not contain only dots.<p> 547 * 548 * @param name the resource name to check 549 * 550 * @throws CmsIllegalArgumentException if the given resource name is not valid 551 */ 552 public static void checkResourceName(String name) throws CmsIllegalArgumentException { 553 554 if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) { 555 throw new CmsIllegalArgumentException( 556 Messages.get().container(Messages.ERR_BAD_RESOURCENAME_EMPTY_0, name)); 557 } 558 559 CmsStringUtil.checkName(name, NAME_CONSTRAINTS, Messages.ERR_BAD_RESOURCENAME_4, Messages.get()); 560 561 // check for filenames that have only dots (which will cause issues in the static export) 562 boolean onlydots = true; 563 // this must be done only for the last name (not for parent folders) 564 String lastName = CmsResource.getName(name); 565 int l = lastName.length(); 566 for (int i = 0; i < l; i++) { 567 char c = lastName.charAt(i); 568 if ((c != '.') && (c != '/')) { 569 onlydots = false; 570 } 571 } 572 if (onlydots) { 573 throw new CmsIllegalArgumentException( 574 Messages.get().container(Messages.ERR_BAD_RESOURCENAME_DOTS_1, lastName)); 575 } 576 } 577 578 /** 579 * Returns the resource name extension if present.<p> 580 * The extension will be always lower case.<p> 581 * 582 * @param resourceName the resource name or path 583 * 584 * @return the extension or <code>null</code> if not available 585 */ 586 public static String getExtension(String resourceName) { 587 588 String result = null; 589 if (!resourceName.endsWith("/")) { 590 // folders don't have any extension 591 resourceName = CmsResource.getName(resourceName); 592 int index = resourceName.lastIndexOf(".") + 1; 593 if ((index > 0) && (index < resourceName.length())) { 594 result = resourceName.substring(index).toLowerCase(); 595 } 596 } 597 598 return result; 599 } 600 601 /** 602 * Returns the folder path of the resource with the given name.<p> 603 * 604 * If the resource name denotes a folder (that is ends with a "/"), the complete path of the folder 605 * is returned (not the parent folder path).<p> 606 * 607 * This is achieved by just cutting of everything behind the last occurrence of a "/" character 608 * in the String, no check if performed if the resource exists or not in the VFS, 609 * only resources that end with a "/" are considered to be folders. 610 * 611 * Example: Returns <code>/system/def/</code> for the 612 * resource <code>/system/def/file.html</code> and 613 * <code>/system/def/</code> for the (folder) resource <code>/system/def/</code>. 614 * 615 * @param resource the name of a resource 616 * @return the folder of the given resource 617 */ 618 public static String getFolderPath(String resource) { 619 620 return resource.substring(0, resource.lastIndexOf('/') + 1); 621 } 622 623 /** 624 * Returns the name of a resource without the path information.<p> 625 * 626 * The resource name of a file is the name of the file. 627 * The resource name of a folder is the folder name with trailing "/". 628 * The resource name of the root folder is <code>/</code>.<p> 629 * 630 * Example: <code>/system/workplace/</code> has the resource name <code>workplace/</code>. 631 * 632 * @param resource the resource to get the name for 633 * @return the name of a resource without the path information 634 */ 635 public static String getName(String resource) { 636 637 if ("/".equals(resource)) { 638 return "/"; 639 } 640 // remove the last char, for a folder this will be "/", for a file it does not matter 641 String parent = (resource.substring(0, resource.length() - 1)); 642 // now as the name does not end with "/", check for the last "/" which is the parent folder name 643 return resource.substring(parent.lastIndexOf('/') + 1); 644 } 645 646 /** 647 * Returns the absolute parent folder name of a resource.<p> 648 * 649 * The parent resource of a file is the folder of the file. 650 * The parent resource of a folder is the parent folder. 651 * The parent resource of the root folder is <code>null</code>.<p> 652 * 653 * Example: <code>/system/workplace/</code> has the parent <code>/system/</code>. 654 * 655 * @param resource the resource to find the parent folder for 656 * @return the calculated parent absolute folder path, or <code>null</code> for the root folder 657 */ 658 public static String getParentFolder(String resource) { 659 660 if (CmsStringUtil.isEmptyOrWhitespaceOnly(resource) || "/".equals(resource)) { 661 return null; 662 } 663 // remove the last char, for a folder this will be "/", for a file it does not matter 664 String parent = (resource.substring(0, resource.length() - 1)); 665 // now as the name does not end with "/", check for the last "/" which is the parent folder name 666 return parent.substring(0, parent.lastIndexOf('/') + 1); 667 } 668 669 /** 670 * Returns the directory level of a resource.<p> 671 * 672 * The root folder "/" has level 0, 673 * a folder "/foo/" would have level 1, 674 * a folfer "/foo/bar/" level 2 etc.<p> 675 * 676 * @param resource the resource to determine the directory level for 677 * @return the directory level of a resource 678 */ 679 public static int getPathLevel(String resource) { 680 681 int level = -1; 682 int pos = 0; 683 while (resource.indexOf('/', pos) >= 0) { 684 pos = resource.indexOf('/', pos) + 1; 685 level++; 686 } 687 return level; 688 } 689 690 /** 691 * Returns the name of a parent folder of the given resource, 692 * that is either minus levels up 693 * from the current folder, or that is plus levels down from the 694 * root folder.<p> 695 * 696 * @param resource the name of a resource 697 * @param level of levels to walk up or down 698 * @return the name of a parent folder of the given resource 699 */ 700 public static String getPathPart(String resource, int level) { 701 702 resource = getFolderPath(resource); 703 String result = null; 704 int pos = 0, count = 0; 705 if (level >= 0) { 706 // Walk down from the root folder / 707 while ((count < level) && (pos > -1)) { 708 count++; 709 pos = resource.indexOf('/', pos + 1); 710 } 711 } else { 712 // Walk up from the current folder 713 pos = resource.length(); 714 while ((count > level) && (pos > -1)) { 715 count--; 716 pos = resource.lastIndexOf('/', pos - 1); 717 } 718 } 719 if (pos > -1) { 720 // To many levels walked 721 result = resource.substring(0, pos + 1); 722 } else { 723 // Add trailing slash 724 result = (level < 0) ? "/" : resource; 725 } 726 return result; 727 } 728 729 /** 730 * Returns true if the resource name certainly denotes a folder, that is ends with a "/".<p> 731 * 732 * @param resource the resource to check 733 * @return true if the resource name certainly denotes a folder, that is ends with a "/" 734 */ 735 public static boolean isFolder(String resource) { 736 737 return CmsStringUtil.isNotEmpty(resource) && (resource.charAt(resource.length() - 1) == '/'); 738 } 739 740 /** 741 * Returns <code>true</code> if the given resource path points to a temporary file name.<p> 742 * 743 * A resource name is considered a temporary file name if the name of the file 744 * (without parent folders) starts with the prefix char <code>'~'</code> (tilde). 745 * Existing parent folder elements are removed from the path before the file name is checked.<p> 746 * 747 * @param path the resource path to check 748 * 749 * @return <code>true</code> if the given resource name is a temporary file name 750 * 751 * @see #isTemporaryFile() 752 */ 753 public static boolean isTemporaryFileName(String path) { 754 755 return (path != null) && getName(path).startsWith(TEMP_FILE_PREFIX); 756 } 757 758 /** 759 * Returns a clone of this Objects instance.<p> 760 * 761 * @return a clone of this instance 762 */ 763 @Override 764 public Object clone() { 765 766 return getCopy(); 767 } 768 769 /** 770 * Uses the resource root path to compare two resources.<p> 771 * 772 * Please note a number of additional comparators for resources exists as members of this class.<p> 773 * 774 * @see java.lang.Comparable#compareTo(java.lang.Object) 775 * 776 * @see #COMPARE_DATE_RELEASED 777 * @see #COMPARE_ROOT_PATH 778 * @see #COMPARE_ROOT_PATH_IGNORE_CASE 779 * @see #COMPARE_ROOT_PATH_IGNORE_CASE_FOLDERS_FIRST 780 */ 781 public int compareTo(I_CmsResource obj) { 782 783 if (obj == this) { 784 return 0; 785 } 786 return m_rootPath.compareTo(obj.getRootPath()); 787 } 788 789 /** 790 * Two resources are considered equal in case their structure id is equal.<p> 791 * 792 * @see java.lang.Object#equals(java.lang.Object) 793 */ 794 @Override 795 public boolean equals(Object obj) { 796 797 if (obj == null) { 798 return false; 799 } 800 801 if (obj == this) { 802 return true; 803 } 804 if (obj instanceof CmsResource) { 805 return ((CmsResource)obj).m_structureId.equals(m_structureId); 806 } 807 return false; 808 } 809 810 /** 811 * Creates a copy of this resource.<p> 812 * 813 * This is useful in case you want to create a copy of a resource and 814 * really make sure won't get a {@link CmsFile} or {@link CmsFolder}, which may happen 815 * if you just call {@link #clone()}.<p> 816 * 817 * @return a copy of this resource 818 */ 819 public CmsResource getCopy() { 820 821 CmsResource result = new CmsResource( 822 m_structureId, 823 m_resourceId, 824 m_rootPath, 825 m_typeId, 826 m_isFolder, 827 m_flags, 828 m_projectLastModified, 829 m_state, 830 m_dateCreated, 831 m_userCreated, 832 m_dateLastModified, 833 m_userLastModified, 834 m_dateReleased, 835 m_dateExpired, 836 m_siblingCount, 837 m_length, 838 m_dateContent, 839 m_version); 840 841 if (isTouched()) { 842 result.setDateLastModified(m_dateLastModified); 843 } 844 845 return result; 846 } 847 848 /** 849 * Returns the date of the last modification of the content of this resource.<p> 850 * 851 * This applies only to resources of type {@link CmsFile}, since a {@link CmsFolder} has no content. 852 * In case of a folder, <code>-1</code> is always returned as content date.<p> 853 * 854 * Any modification of a resource, including changes to the resource properties, 855 * will increase the "date of last modification" which is returned by {@link #getDateLastModified()}. 856 * The "date of the content" as returned by this method only changes when the 857 * file content as returned by {@link CmsFile#getContents()} is changed.<p> 858 * 859 * @return the date of the last modification of the content of this resource 860 * 861 * @since 7.0.0 862 */ 863 public long getDateContent() { 864 865 return m_dateContent; 866 } 867 868 /** 869 * Returns the date of the creation of this resource.<p> 870 * 871 * @return the date of the creation of this resource 872 */ 873 public long getDateCreated() { 874 875 return m_dateCreated; 876 } 877 878 /** 879 * Returns the expiration date this resource.<p> 880 * 881 * If the expiration date has not been set, {@link #DATE_EXPIRED_DEFAULT} is returned. 882 * This means: The resource does never expire.<p> 883 * 884 * @return the expiration date of this resource 885 */ 886 public long getDateExpired() { 887 888 return m_dateExpired; 889 } 890 891 /** 892 * Returns the date of the last modification of this resource.<p> 893 * 894 * @return the date of the last modification of this resource 895 */ 896 public long getDateLastModified() { 897 898 return m_dateLastModified; 899 } 900 901 /** 902 * Returns the release date this resource.<p> 903 * 904 * If the release date has not been set, {@link #DATE_RELEASED_DEFAULT} is returned. 905 * This means: The resource has always been released.<p> 906 * 907 * @return the release date of this resource 908 */ 909 public long getDateReleased() { 910 911 return m_dateReleased; 912 } 913 914 /** 915 * Returns the flags of this resource.<p> 916 * 917 * @return the flags of this resource 918 * 919 * @see #setFlags(int) for an explanation of the resource flags 920 */ 921 public int getFlags() { 922 923 return m_flags; 924 } 925 926 /** 927 * Returns the content length of this resource.<p> 928 * 929 * If the resource is a file, then this is the byte size of the file content. 930 * If the resource is a folder, then the size is always -1.<p> 931 * 932 * @return the content length of the content 933 */ 934 public int getLength() { 935 936 // make sure folders always have a -1 size 937 return m_isFolder ? -1 : m_length; 938 } 939 940 /** 941 * Returns the file name of this resource without parent folders, for example <code>index.html</code>.<p> 942 * 943 * @return the file name of this resource without parent folders 944 */ 945 public String getName() { 946 947 String name = getName(m_rootPath); 948 if (name.charAt(name.length() - 1) == '/') { 949 return name.substring(0, name.length() - 1); 950 } else { 951 return name; 952 } 953 } 954 955 /** 956 * Returns the id of the {@link CmsProject} where this resource has been last modified.<p> 957 * 958 * @return the id of the {@link CmsProject} where this resource has been last modified, or <code>null</code> 959 */ 960 public CmsUUID getProjectLastModified() { 961 962 return m_projectLastModified; 963 } 964 965 /** 966 * Returns the id of the database content record of this resource.<p> 967 * 968 * @return the id of the database content record of this resource 969 */ 970 public CmsUUID getResourceId() { 971 972 return m_resourceId; 973 } 974 975 /** 976 * Returns the name of this resource with it's full path from the top level root folder, 977 * for example <code>/sites/default/myfolder/index.html</code>.<p> 978 * 979 * In a presentation level application usually the current site root must be 980 * cut of from the root path. Use {@link CmsObject#getSitePath(CmsResource)} 981 * to get the "absolute" path of a resource in the current site.<p> 982 * 983 * @return the name of this resource with it's full path from the top level root folder 984 * 985 * @see CmsObject#getSitePath(CmsResource) 986 * @see CmsRequestContext#getSitePath(CmsResource) 987 * @see CmsRequestContext#removeSiteRoot(String) 988 */ 989 public String getRootPath() { 990 991 return m_rootPath; 992 } 993 994 /** 995 * Returns the number of siblings of this resource, also counting this resource.<p> 996 * 997 * If a resource has no sibling, the total sibling count for this resource is <code>1</code>, 998 * if a resource has <code>n</code> siblings, the sibling count is <code>n + 1</code>.<p> 999 * 1000 * @return the number of siblings of this resource, also counting this resource 1001 */ 1002 public int getSiblingCount() { 1003 1004 return m_siblingCount; 1005 } 1006 1007 /** 1008 * Returns the state of this resource.<p> 1009 * 1010 * This may be {@link CmsResource#STATE_UNCHANGED}, 1011 * {@link CmsResource#STATE_CHANGED}, {@link CmsResource#STATE_NEW} 1012 * or {@link CmsResource#STATE_DELETED}.<p> 1013 * 1014 * @return the state of this resource 1015 */ 1016 public CmsResourceState getState() { 1017 1018 return m_state; 1019 } 1020 1021 /** 1022 * Returns the id of the database structure record of this resource.<p> 1023 * 1024 * @return the id of the database structure record of this resource 1025 */ 1026 public CmsUUID getStructureId() { 1027 1028 return m_structureId; 1029 } 1030 1031 /** 1032 * Returns the resource type id for this resource.<p> 1033 * 1034 * @return the resource type id of this resource 1035 */ 1036 public int getTypeId() { 1037 1038 return m_typeId; 1039 } 1040 1041 /** 1042 * Returns the user id of the {@link CmsUser} who created this resource.<p> 1043 * 1044 * @return the user id of the {@link CmsUser} who created this resource 1045 */ 1046 public CmsUUID getUserCreated() { 1047 1048 return m_userCreated; 1049 } 1050 1051 /** 1052 * Returns the id of the {@link CmsUser} who made the last modification on this resource.<p> 1053 * 1054 * @return the id of the {@link CmsUser} who made the last modification on this resource 1055 */ 1056 public CmsUUID getUserLastModified() { 1057 1058 return m_userLastModified; 1059 } 1060 1061 /** 1062 * Returns the current version number of this resource.<p> 1063 * 1064 * @return the current version number of this resource 1065 */ 1066 public int getVersion() { 1067 1068 return m_version; 1069 } 1070 1071 /** 1072 * @see java.lang.Object#hashCode() 1073 */ 1074 @Override 1075 public int hashCode() { 1076 1077 if (m_structureId != null) { 1078 return m_structureId.hashCode(); 1079 } 1080 1081 return CmsUUID.getNullUUID().hashCode(); 1082 } 1083 1084 /** 1085 * Returns <code>true</code> if this resource is expired at the given time according to the 1086 * information stored in {@link #getDateExpired()}.<p> 1087 * 1088 * @param time the time to check the expiration date against 1089 * 1090 * @return <code>true</code> if this resource is expired at the given time 1091 * 1092 * @see #isReleased(long) 1093 * @see #isReleasedAndNotExpired(long) 1094 * @see #DATE_RELEASED_EXPIRED_IGNORE 1095 * @see CmsResource#getDateReleased() 1096 * @see CmsRequestContext#getRequestTime() 1097 */ 1098 public boolean isExpired(long time) { 1099 1100 return (time > m_dateExpired) && (time != DATE_RELEASED_EXPIRED_IGNORE); 1101 } 1102 1103 /** 1104 * Returns <code>true</code> if the resource is a {@link CmsFile}, that is not a {@link CmsFolder}.<p> 1105 * 1106 * @return true if this resource is a file, false otherwise 1107 */ 1108 public boolean isFile() { 1109 1110 return !m_isFolder; 1111 } 1112 1113 /** 1114 * Returns <code>true</code> if the resource is a {@link CmsFolder}, that is not a {@link CmsFile}.<p> 1115 * 1116 * @return true if this resource is a folder, false otherwise 1117 */ 1118 public boolean isFolder() { 1119 1120 return m_isFolder; 1121 } 1122 1123 /** 1124 * Returns <code>true</code> if the resource is marked as internal.<p> 1125 * 1126 * An internal resource can be read by the OpenCms API, but it can not be delivered 1127 * by a direct request from an outside user.<p> 1128 * 1129 * For example if the resource <code>/internal.xml</code> 1130 * has been set as marked as internal, this resource can not be requested by an HTTP request, 1131 * so when a user enters <code>http:/www.myserver.com/opencms/opencms/internal.xml</code> in the browser 1132 * this will generate a {@link CmsVfsResourceNotFoundException}.<p> 1133 * 1134 * This state is stored as bit 1 in the resource flags.<p> 1135 * 1136 * @return <code>true</code> if the resource is internal 1137 */ 1138 public boolean isInternal() { 1139 1140 return ((m_flags & FLAG_INTERNAL) > 0); 1141 } 1142 1143 /** 1144 * Returns <code>true</code> if the resource has to be labeled with a special icon in the explorer view.<p> 1145 * 1146 * This state is stored as bit 2 in the resource flags.<p> 1147 * 1148 * @return <code>true</code> if the resource has to be labeled in the explorer view 1149 */ 1150 public boolean isLabeled() { 1151 1152 return ((m_flags & CmsResource.FLAG_LABELED) > 0); 1153 } 1154 1155 /** 1156 * Returns <code>true</code> if this resource is released at the given time according to the 1157 * information stored in {@link #getDateReleased()}.<p> 1158 * 1159 * @param time the time to check the release date against 1160 * 1161 * @return <code>true</code> if this resource is released at the given time 1162 * 1163 * @see #isExpired(long) 1164 * @see #isReleasedAndNotExpired(long) 1165 * @see #DATE_RELEASED_EXPIRED_IGNORE 1166 * @see CmsResource#getDateReleased() 1167 * @see CmsRequestContext#getRequestTime() 1168 */ 1169 public boolean isReleased(long time) { 1170 1171 return (time > m_dateReleased) || (time == DATE_RELEASED_EXPIRED_IGNORE); 1172 } 1173 1174 /** 1175 * Returns <code>true</code> if this resource is valid at the given time according to the 1176 * information stored in {@link #getDateReleased()} and {@link #getDateExpired()}.<p> 1177 * 1178 * A resource is valid if it is released and not yet expired.<p> 1179 * 1180 * @param time the time to check the release and expiration date against 1181 * 1182 * @return <code>true</code> if this resource is valid at the given time 1183 * 1184 * @see #isExpired(long) 1185 * @see #isReleased(long) 1186 * @see #DATE_RELEASED_EXPIRED_IGNORE 1187 * @see CmsResource#getDateReleased() 1188 * @see CmsRequestContext#getRequestTime() 1189 */ 1190 public boolean isReleasedAndNotExpired(long time) { 1191 1192 return ((time < m_dateExpired) && (time > m_dateReleased)) || (time == DATE_RELEASED_EXPIRED_IGNORE); 1193 } 1194 1195 /** 1196 * Returns <code>true</code> if this resource is a temporary file.<p> 1197 * 1198 * A resource is considered a temporary file it is a file where the 1199 * {@link CmsResource#FLAG_TEMPFILE} flag has been set, or if the file name (without parent folders) 1200 * starts with the prefix char <code>'~'</code> (tilde).<p> 1201 * 1202 * @return <code>true</code> if the given resource name is a temporary file 1203 * 1204 * @see #isTemporaryFileName(String) 1205 */ 1206 public boolean isTemporaryFile() { 1207 1208 return isFile() && (((getFlags() & CmsResource.FLAG_TEMPFILE) > 0) || isTemporaryFileName(getName())); 1209 } 1210 1211 /** 1212 * Returns <code>true</code> if this resource was touched.<p> 1213 * 1214 * @return <code>true</code> if this resource was touched 1215 */ 1216 public boolean isTouched() { 1217 1218 return m_isTouched; 1219 } 1220 1221 /** 1222 * Sets the expiration date this resource.<p> 1223 * 1224 * @param time the expiration date to set 1225 */ 1226 public void setDateExpired(long time) { 1227 1228 m_dateExpired = time; 1229 } 1230 1231 /** 1232 * Sets the date of the last modification of this resource.<p> 1233 * 1234 * @param time the last modification date to set 1235 */ 1236 public void setDateLastModified(long time) { 1237 1238 m_isTouched = true; 1239 m_dateLastModified = time; 1240 } 1241 1242 /** 1243 * Sets the release date this resource.<p> 1244 * 1245 * @param time the release date to set 1246 */ 1247 public void setDateReleased(long time) { 1248 1249 m_dateReleased = time; 1250 } 1251 1252 /** 1253 * Sets the flags of this resource.<p> 1254 * 1255 * The resource flags integer is used as bit set that contains special information about the resource. 1256 * The following methods internally use the resource flags:<ul> 1257 * <li>{@link #isInternal()} 1258 * <li>{@link #isLabeled()} 1259 * </ul> 1260 * 1261 * @param flags the flags value to set 1262 */ 1263 public void setFlags(int flags) { 1264 1265 m_flags = flags; 1266 } 1267 1268 /** 1269 * Sets or clears the internal flag.<p> 1270 * 1271 * @param internal true if the internal flag should be set, false if it should be cleared 1272 */ 1273 public void setInternal(boolean internal) { 1274 1275 m_flags = (m_flags & ~FLAG_INTERNAL) | (internal ? FLAG_INTERNAL : 0); 1276 } 1277 1278 /** 1279 * Sets the state of this resource.<p> 1280 * 1281 * @param state the state to set 1282 */ 1283 public void setState(CmsResourceState state) { 1284 1285 m_state = state; 1286 } 1287 1288 /** 1289 * Sets the type of this resource.<p> 1290 * 1291 * @param type the type to set 1292 */ 1293 public void setType(int type) { 1294 1295 m_typeId = type; 1296 } 1297 1298 /** 1299 * Sets the user id of the user who changed this resource.<p> 1300 * 1301 * @param resourceLastModifiedByUserId the user id of the user who changed the resource 1302 */ 1303 public void setUserLastModified(CmsUUID resourceLastModifiedByUserId) { 1304 1305 m_userLastModified = resourceLastModifiedByUserId; 1306 } 1307 1308 /** 1309 * @see java.lang.Object#toString() 1310 */ 1311 @Override 1312 public String toString() { 1313 1314 StringBuffer result = new StringBuffer(); 1315 1316 result.append("["); 1317 result.append(this.getClass().getName()); 1318 result.append(", path: "); 1319 result.append(m_rootPath); 1320 result.append(", structure id "); 1321 result.append(m_structureId); 1322 result.append(", resource id: "); 1323 result.append(m_resourceId); 1324 result.append(", type id: "); 1325 result.append(m_typeId); 1326 result.append(", folder: "); 1327 result.append(m_isFolder); 1328 result.append(", flags: "); 1329 result.append(m_flags); 1330 result.append(", project: "); 1331 result.append(m_projectLastModified); 1332 result.append(", state: "); 1333 result.append(m_state); 1334 result.append(", date created: "); 1335 result.append(new java.util.Date(m_dateCreated)); 1336 result.append(", user created: "); 1337 result.append(m_userCreated); 1338 result.append(", date lastmodified: "); 1339 result.append(new java.util.Date(m_dateLastModified)); 1340 result.append(", user lastmodified: "); 1341 result.append(m_userLastModified); 1342 result.append(", date released: "); 1343 result.append(new java.util.Date(m_dateReleased)); 1344 result.append(", date expired: "); 1345 result.append(new java.util.Date(m_dateExpired)); 1346 result.append(", date content: "); 1347 result.append(new java.util.Date(m_dateContent)); 1348 result.append(", size: "); 1349 result.append(m_length); 1350 result.append(", sibling count: "); 1351 result.append(m_siblingCount); 1352 result.append(", version: "); 1353 result.append(m_version); 1354 result.append("]"); 1355 1356 return result.toString(); 1357 } 1358}