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.db; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.file.CmsVfsResourceNotFoundException; 035import org.opencms.file.I_CmsResource; 036import org.opencms.main.CmsException; 037import org.opencms.main.CmsIllegalArgumentException; 038import org.opencms.main.CmsLog; 039import org.opencms.main.OpenCms; 040import org.opencms.util.CmsFileUtil; 041import org.opencms.util.CmsUUID; 042 043import java.io.Externalizable; 044import java.io.IOException; 045import java.io.ObjectInput; 046import java.io.ObjectOutput; 047import java.util.ArrayList; 048import java.util.Collection; 049import java.util.Collections; 050import java.util.HashMap; 051import java.util.HashSet; 052import java.util.Iterator; 053import java.util.List; 054import java.util.Map; 055import java.util.Set; 056 057import org.apache.commons.logging.Log; 058 059/** 060 * A container for all new/changed/deteled Cms resources that are published together.<p> 061 * 062 * Only classes inside the org.opencms.db package can add or remove elements to or from this list. 063 * This allows the OpenCms API to pass the list around between classes, but with restricted access to 064 * create this list.<p> 065 * 066 * To create a publish list, one of the public constructors must be used in order to set the basic operation mode 067 * (project publish or direct publish). 068 * After this, use <code>{@link org.opencms.db.CmsDriverManager#fillPublishList(CmsDbContext, CmsPublishList)}</code> 069 * to fill the actual values of the publish list.<p> 070 * 071 * @since 6.0.0 072 * 073 * @see org.opencms.db.CmsDriverManager#fillPublishList(CmsDbContext, CmsPublishList) 074 */ 075public class CmsPublishList implements Externalizable { 076 077 /** The log object for this class. */ 078 private static final Log LOG = CmsLog.getLog(CmsPublishList.class); 079 080 /** Indicates a non existent object in the serialized data. */ 081 private static final int NIL = -1; 082 083 /** Serial version UID required for safe serialization. */ 084 private static final long serialVersionUID = -2578909250462750927L; 085 086 /** Length of a serialized uuid. */ 087 private static final int UUID_LENGTH = CmsUUID.getNullUUID().toByteArray().length; 088 089 /** The list of deleted folder resources to be published.<p> */ 090 private List<CmsResource> m_deletedFolderList; 091 092 /** The list of deleted folder UUIDs to be published for later retrieval.<p> */ 093 private List<CmsUUID> m_deletedFolderUUIDs; 094 095 /** The list of direct publish resources. */ 096 private List<CmsResource> m_directPublishResources; 097 098 /** The list of direct publish resource UUIDs to be published for later retrieval.<p> */ 099 private List<CmsUUID> m_directPublishResourceUUIDs; 100 101 /** The list of new/changed/deleted file resources to be published.<p> */ 102 private List<CmsResource> m_fileList; 103 104 /** The list of new/changed/deleted file resource UUIDs to be published for later retrieval.<p> */ 105 private List<CmsUUID> m_fileUUIDs; 106 107 /** The list of new/changed folder resources to be published.<p> */ 108 private List<CmsResource> m_folderList; 109 110 /** The list of new/changed folder resource UUIDs to be published for later retrieval.<p> */ 111 private List<CmsUUID> m_folderUUIDs; 112 113 /** Indicates whether this is a user publish list. */ 114 private boolean m_isUserPublishList; 115 116 /** Flag to indicate if the list needs to be revived. */ 117 private boolean m_needsRevive; 118 119 /** The id of the project that is to be published. */ 120 private CmsUUID m_projectId; 121 122 /** The publish history ID.<p> */ 123 private CmsUUID m_publishHistoryId; 124 125 /** Indicates if siblings of the resources in the list should also be published. */ 126 private boolean m_publishSiblings; 127 128 /** Indicates if sub-resources in folders should be published (for direct publish only). */ 129 private boolean m_publishSubResources; 130 131 /** 132 * Empty constructor.<p> 133 */ 134 public CmsPublishList() { 135 136 // noop 137 } 138 139 /** 140 * Constructs a publish list for a list of direct publish resources.<p> 141 * 142 * @param all no redundant resource are filtered out 143 * @param directPublishResources a list of <code>{@link CmsResource}</code> instances to be published directly 144 * @param directPublishSiblings indicates if all siblings of the selected resources should be published 145 */ 146 public CmsPublishList(boolean all, List<CmsResource> directPublishResources, boolean directPublishSiblings) { 147 148 this(null, directPublishResources, directPublishSiblings, false, true); 149 } 150 151 /** 152 * Constructs a publish list for a given project.<p> 153 * 154 * @param project the project to publish, this should always be the id of the current project 155 */ 156 public CmsPublishList(CmsProject project) { 157 158 this(project, null, false, true, false); 159 } 160 161 /** 162 * Constructs a publish list for a single direct publish resource.<p> 163 * 164 * @param directPublishResource a VFS resource to be published directly 165 * @param publishSiblings indicates if all siblings of the selected resources should be published 166 */ 167 public CmsPublishList(CmsResource directPublishResource, boolean publishSiblings) { 168 169 this(null, Collections.singletonList(directPublishResource), publishSiblings, true, false); 170 } 171 172 /** 173 * Constructs a publish list for a list of direct publish resources.<p> 174 * 175 * @param directPublishResources a list of <code>{@link CmsResource}</code> instances to be published directly 176 * @param publishSiblings indicates if all siblings of the selected resources should be published 177 */ 178 public CmsPublishList(List<CmsResource> directPublishResources, boolean publishSiblings) { 179 180 this(null, directPublishResources, publishSiblings, true, false); 181 } 182 183 /** 184 * Constructs a publish list for a list of direct publish resources.<p> 185 * 186 * @param directPublishResources a list of <code>{@link CmsResource}</code> instances to be published directly 187 * @param publishSiblings indicates if all siblings of the selected resources should be published 188 * @param publishSubResources indicates if sub-resources in folders should be published (for direct publish only) 189 */ 190 public CmsPublishList( 191 List<CmsResource> directPublishResources, 192 boolean publishSiblings, 193 boolean publishSubResources) { 194 195 this(null, directPublishResources, publishSiblings, publishSubResources, false); 196 } 197 198 /** 199 * Internal constructor for a publish list.<p> 200 * 201 * @param project the project to publish 202 * @param directPublishResources the list of direct publish resources 203 * @param publishSiblings indicates if all siblings of the selected resources should be published 204 * @param publishSubResources indicates if sub-resources in folders should be published (for direct publish only) 205 * @param all if <code>true</code> the publish list will not be filtered for redundant resources 206 */ 207 private CmsPublishList( 208 CmsProject project, 209 List<CmsResource> directPublishResources, 210 boolean publishSiblings, 211 boolean publishSubResources, 212 boolean all) { 213 214 m_fileList = new ArrayList<CmsResource>(); 215 m_folderList = new ArrayList<CmsResource>(); 216 m_deletedFolderList = new ArrayList<CmsResource>(); 217 m_publishHistoryId = new CmsUUID(); 218 m_publishSiblings = publishSiblings; 219 m_publishSubResources = publishSubResources; 220 m_projectId = (project != null) ? project.getUuid() : null; 221 if (directPublishResources != null) { 222 if (!all) { 223 // reduce list of folders to minimum 224 m_directPublishResources = Collections.unmodifiableList( 225 CmsFileUtil.removeRedundantResources(directPublishResources)); 226 } else { 227 m_directPublishResources = Collections.unmodifiableList(directPublishResources); 228 } 229 } 230 } 231 232 /** 233 * Returns a list of all resources in the publish list, 234 * including folders and files.<p> 235 * 236 * @return a list of {@link CmsResource} objects 237 */ 238 public List<CmsResource> getAllResources() { 239 240 List<CmsResource> all = new ArrayList<CmsResource>(); 241 all.addAll(m_folderList); 242 all.addAll(m_fileList); 243 all.addAll(m_deletedFolderList); 244 245 Collections.sort(all, I_CmsResource.COMPARE_ROOT_PATH); 246 return Collections.unmodifiableList(all); 247 } 248 249 /** 250 * Returns a list of folder resources with the deleted state.<p> 251 * 252 * @return a list of folder resources with the deleted state 253 */ 254 public List<CmsResource> getDeletedFolderList() { 255 256 if (m_needsRevive) { 257 return null; 258 } else { 259 return m_deletedFolderList; 260 } 261 } 262 263 /** 264 * Returns the list of resources that should be published for a "direct" publish operation.<p> 265 * 266 * Will return <code>null</code> if this publish list was not initialized for a "direct publish" but 267 * for a project publish.<p> 268 * 269 * @return the list of resources that should be published for a "direct" publish operation, or <code>null</code> 270 */ 271 public List<CmsResource> getDirectPublishResources() { 272 273 if (m_needsRevive) { 274 return null; 275 } else { 276 return m_directPublishResources; 277 } 278 } 279 280 /** 281 * Returns an unmodifiable list of the files in this publish list.<p> 282 * 283 * @return the list with the files in this publish list 284 */ 285 public List<CmsResource> getFileList() { 286 287 if (m_needsRevive) { 288 return null; 289 } else { 290 return Collections.unmodifiableList(m_fileList); 291 } 292 } 293 294 /** 295 * Returns an unmodifiable list of the new/changed folders in this publish list.<p> 296 * 297 * @return the list with the new/changed folders in this publish list 298 */ 299 public List<CmsResource> getFolderList() { 300 301 if (m_needsRevive) { 302 return null; 303 } else { 304 return Collections.unmodifiableList(m_folderList); 305 } 306 } 307 308 /** 309 * Returns the id of the project that should be published, or <code>-1</code> if this publish list 310 * is initialized for a "direct publish" operation.<p> 311 * 312 * @return the id of the project that should be published, or <code>-1</code> 313 */ 314 public CmsUUID getProjectId() { 315 316 return m_projectId; 317 } 318 319 /** 320 * Returns the publish history Id for this publish list.<p> 321 * 322 * @return the publish history Id 323 */ 324 public CmsUUID getPublishHistoryId() { 325 326 return m_publishHistoryId; 327 } 328 329 /** 330 * Gets the list of moved folders which are not subfolders of other moved folders in the publish list.<p> 331 * @param cms the current cms context 332 * @return the moved folders which are not subfolders of other moved folders in the publish list 333 * @throws CmsException if something goes wrong 334 */ 335 public List<CmsResource> getTopMovedFolders(CmsObject cms) throws CmsException { 336 337 List<CmsResource> movedFolders = getMovedFolders(cms); 338 List<CmsResource> result = getTopFolders(movedFolders); 339 return result; 340 } 341 342 /** 343 * Checks if this is a publish list is used for a "direct publish" operation.<p> 344 * 345 * @return true if this is a publish list is used for a "direct publish" operation 346 */ 347 public boolean isDirectPublish() { 348 349 return (m_projectId == null); 350 } 351 352 /** 353 * Returns <code>true</code> if all siblings of the project resources are to be published.<p> 354 * 355 * @return <code>true</code> if all siblings of the project resources are to be publisheds 356 */ 357 public boolean isPublishSiblings() { 358 359 return m_publishSiblings; 360 } 361 362 /** 363 * Returns <code>true</code> if sub-resources in folders should be published (for direct publish only).<p> 364 * 365 * @return <code>true</code> if sub-resources in folders should be published (for direct publish only) 366 */ 367 public boolean isPublishSubResources() { 368 369 return m_publishSubResources; 370 } 371 372 /** 373 * Returns true if this is a user publish list.<p> 374 * 375 * @return true if this is a user publish list 376 */ 377 public boolean isUserPublishList() { 378 379 return m_isUserPublishList; 380 } 381 382 /** 383 * @see java.io.Externalizable#readExternal(java.io.ObjectInput) 384 */ 385 public void readExternal(ObjectInput in) throws IOException { 386 387 // read the history id 388 m_publishHistoryId = internalReadUUID(in); 389 // read the project id 390 m_projectId = internalReadUUID(in); 391 if (m_projectId.isNullUUID()) { 392 m_projectId = null; 393 } 394 // read the flags 395 m_publishSiblings = (in.readInt() != 0); 396 m_publishSubResources = (in.readInt() != 0); 397 // read the list of direct published resources 398 m_directPublishResourceUUIDs = internalReadUUIDList(in); 399 // read the list of published files 400 m_fileUUIDs = internalReadUUIDList(in); 401 // read the list of published folders 402 m_folderUUIDs = internalReadUUIDList(in); 403 // read the list of deleted folders 404 m_deletedFolderUUIDs = internalReadUUIDList(in); 405 // set revive flag to indicate that resource lists must be revived 406 m_needsRevive = true; 407 } 408 409 /** 410 * Revives the publish list by populating the internal resource lists with <code>{@link CmsResource}</code> instances.<p> 411 * 412 * @param cms a cms object used to read the resource instances 413 */ 414 public void revive(CmsObject cms) { 415 416 if (m_needsRevive) { 417 if (m_directPublishResourceUUIDs != null) { 418 m_directPublishResources = internalReadResourceList(cms, m_directPublishResourceUUIDs); 419 } 420 if (m_fileUUIDs != null) { 421 m_fileList = internalReadResourceList(cms, m_fileUUIDs); 422 } 423 if (m_folderUUIDs != null) { 424 m_folderList = internalReadResourceList(cms, m_folderUUIDs); 425 } 426 if (m_deletedFolderUUIDs != null) { 427 m_deletedFolderList = internalReadResourceList(cms, m_deletedFolderUUIDs); 428 } 429 m_needsRevive = false; 430 } 431 } 432 433 /** 434 * Sets the 'user publish list' flag on this publish list.<p> 435 * 436 * @param isUserPublishList if true, the list is marked as a user publish list 437 */ 438 public void setUserPublishList(boolean isUserPublishList) { 439 440 m_isUserPublishList = isUserPublishList; 441 } 442 443 /** 444 * Returns the number of all resources to be published.<p> 445 * 446 * @return the number of all resources to be published 447 */ 448 public int size() { 449 450 if (m_needsRevive) { 451 return 0; 452 } else { 453 return m_folderList.size() + m_fileList.size() + m_deletedFolderList.size(); 454 } 455 } 456 457 /** 458 * @see java.lang.Object#toString() 459 */ 460 @Override 461 public String toString() { 462 463 StringBuffer result = new StringBuffer(); 464 result.append("\n[\n"); 465 if (isDirectPublish()) { 466 result.append("direct publish of resources: ").append(m_directPublishResources.toString()).append("\n"); 467 } else { 468 result.append("publish of project: ").append(m_projectId).append("\n"); 469 } 470 result.append("publish history ID: ").append(m_publishHistoryId.toString()).append("\n"); 471 result.append("resources: ").append(m_fileList.toString()).append("\n"); 472 result.append("folders: ").append(m_folderList.toString()).append("\n"); 473 result.append("deletedFolders: ").append(m_deletedFolderList.toString()).append("\n"); 474 result.append("]\n"); 475 return result.toString(); 476 } 477 478 /** 479 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) 480 */ 481 public void writeExternal(ObjectOutput out) throws IOException { 482 483 // write the history id 484 out.write(m_publishHistoryId.toByteArray()); 485 // write the project id 486 out.write((m_projectId != null) ? m_projectId.toByteArray() : CmsUUID.getNullUUID().toByteArray()); 487 // write the flags 488 out.writeInt((m_publishSiblings) ? 1 : 0); 489 out.writeInt((m_publishSubResources) ? 1 : 0); 490 // write the list of direct publish resources by writing the uuid of each resource 491 if (m_directPublishResources != null) { 492 out.writeInt(m_directPublishResources.size()); 493 for (Iterator<CmsResource> i = m_directPublishResources.iterator(); i.hasNext();) { 494 out.write((i.next()).getStructureId().toByteArray()); 495 } 496 } else { 497 out.writeInt(NIL); 498 } 499 // write the list of published files by writing the uuid of each resource 500 if (m_fileList != null) { 501 out.writeInt(m_fileList.size()); 502 for (Iterator<CmsResource> i = m_fileList.iterator(); i.hasNext();) { 503 out.write((i.next()).getStructureId().toByteArray()); 504 } 505 } else { 506 out.writeInt(NIL); 507 } 508 // write the list of published folders by writing the uuid of each resource 509 if (m_folderList != null) { 510 out.writeInt(m_folderList.size()); 511 for (Iterator<CmsResource> i = m_folderList.iterator(); i.hasNext();) { 512 out.write((i.next()).getStructureId().toByteArray()); 513 } 514 } else { 515 out.writeInt(NIL); 516 } 517 // write the list of deleted folders by writing the uuid of each resource 518 if (m_deletedFolderList != null) { 519 out.writeInt(m_deletedFolderList.size()); 520 for (Iterator<CmsResource> i = m_deletedFolderList.iterator(); i.hasNext();) { 521 out.write((i.next()).getStructureId().toByteArray()); 522 } 523 } else { 524 out.writeInt(NIL); 525 } 526 } 527 528 /** 529 * Adds a new/changed Cms folder resource to the publish list.<p> 530 * 531 * @param resource a new/changed Cms folder resource 532 * @param check if set an exception is thrown if the specified resource is unchanged, 533 * if not set the resource is ignored 534 * 535 * @throws IllegalArgumentException if the specified resource is unchanged 536 */ 537 protected void add(CmsResource resource, boolean check) throws IllegalArgumentException { 538 539 if (check) { 540 // it is essential that this method is only visible within the db package! 541 if (resource.getState().isUnchanged()) { 542 throw new CmsIllegalArgumentException( 543 Messages.get().container(Messages.ERR_PUBLISH_UNCHANGED_RESOURCE_1, resource.getRootPath())); 544 } 545 } 546 if (resource.isFolder()) { 547 if (resource.getState().isDeleted()) { 548 if (!m_deletedFolderList.contains(resource)) { 549 // only add files not already contained in the list 550 m_deletedFolderList.add(resource); 551 } 552 } else { 553 if (!m_folderList.contains(resource)) { 554 // only add files not already contained in the list 555 m_folderList.add(resource); 556 } 557 } 558 } else { 559 if (!m_fileList.contains(resource)) { 560 // only add files not already contained in the list 561 // this is required to make sure no siblings are duplicated 562 m_fileList.add(resource); 563 } 564 } 565 } 566 567 /** 568 * Appends all the given resources to this publish list.<p> 569 * 570 * @param resources resources to be added to this publish list 571 * @param check if set an exception is thrown if the a resource is unchanged, 572 * if not set the resource is ignored 573 * 574 * @throws IllegalArgumentException if one of the resources is unchanged 575 */ 576 protected void addAll(Collection<CmsResource> resources, boolean check) throws IllegalArgumentException { 577 578 // it is essential that this method is only visible within the db package! 579 Iterator<CmsResource> i = resources.iterator(); 580 while (i.hasNext()) { 581 add(i.next(), check); 582 } 583 } 584 585 /** 586 * Checks whether the publish list contains all sub-resources of a list of folders.<p> 587 * 588 * @param cms the current CMS context 589 * @param folders the folders which should be checked 590 * @return a folder from the list if one of its sub-resources is not contained in the publish list, otherwise null 591 * 592 * @throws CmsException if something goes wrong 593 */ 594 protected CmsResource checkContainsSubResources(CmsObject cms, List<CmsResource> folders) throws CmsException { 595 596 for (CmsResource folder : folders) { 597 598 if (!containsSubResources(cms, folder)) { 599 return folder; 600 } 601 } 602 return null; 603 } 604 605 /** 606 * Checks if the publish list contains a resource.<p> 607 * 608 * @param res the resource 609 * @return true if the publish list contains a resource 610 */ 611 protected boolean containsResource(CmsResource res) { 612 613 return m_deletedFolderList.contains(res) || m_folderList.contains(res) || m_fileList.contains(res); 614 } 615 616 /** 617 * Checks if the publish list contains all sub-resources of a given folder.<p> 618 * 619 * @param cms the current CMS context 620 * @param folder the folder for which the check should be performed 621 * @return true if the publish list contains all sub-resources of a given folder 622 * @throws CmsException if something goes wrong 623 */ 624 protected boolean containsSubResources(CmsObject cms, CmsResource folder) throws CmsException { 625 626 String folderPath = cms.getSitePath(folder); 627 List<CmsResource> subResources = cms.readResources(folderPath, CmsResourceFilter.ALL, true); 628 for (CmsResource resource : subResources) { 629 if (!containsResource(resource)) { 630 return false; 631 } 632 } 633 return true; 634 } 635 636 /** 637 * Gets the sub-resources of a list of folders which are missing from the publish list.<p> 638 * 639 * @param cms the current CMS context 640 * @param folders the folders which should be checked 641 * @return a list of missing sub resources 642 * 643 * @throws CmsException if something goes wrong 644 */ 645 protected List<CmsResource> getMissingSubResources(CmsObject cms, List<CmsResource> folders) throws CmsException { 646 647 List<CmsResource> result = new ArrayList<CmsResource>(); 648 CmsObject rootCms = OpenCms.initCmsObject(cms); 649 rootCms.getRequestContext().setSiteRoot(""); 650 for (CmsResource folder : folders) { 651 List<CmsResource> subResources = rootCms.readResources(folder.getRootPath(), CmsResourceFilter.ALL, true); 652 for (CmsResource resource : subResources) { 653 if (!containsResource(resource) && !resource.getState().isNew()) { 654 result.add(resource); 655 } 656 } 657 } 658 return result; 659 } 660 661 /** 662 * Internal method to get the moved folders from the publish list.<p> 663 * 664 * @param cms the current CMS context 665 * @return the list of moved folders from the publish list 666 * @throws CmsException if something goes wrong 667 */ 668 protected List<CmsResource> getMovedFolders(CmsObject cms) throws CmsException { 669 670 CmsProject onlineProject = cms.readProject(CmsProject.ONLINE_PROJECT_ID); 671 List<CmsResource> movedFolders = new ArrayList<CmsResource>(); 672 for (CmsResource folder : m_folderList) { 673 if (folder.getState().isChanged()) { 674 CmsProject oldProject = cms.getRequestContext().getCurrentProject(); 675 boolean isMoved = false; 676 try { 677 cms.getRequestContext().setCurrentProject(onlineProject); 678 CmsResource onlineResource = cms.readResource(folder.getStructureId()); 679 isMoved = !onlineResource.getRootPath().equals(folder.getRootPath()); 680 } catch (CmsVfsResourceNotFoundException e) { 681 // resource not found online, this means it doesn't matter whether it has been moved 682 } finally { 683 cms.getRequestContext().setCurrentProject(oldProject); 684 } 685 if (isMoved) { 686 movedFolders.add(folder); 687 } 688 } 689 } 690 return movedFolders; 691 } 692 693 /** 694 * Gives the "roots" of a list of folders, i.e. the list of folders which are not descendants of any other folders in the original list 695 * @param folders the original list of folders 696 * @return the root folders of the list 697 */ 698 protected List<CmsResource> getTopFolders(List<CmsResource> folders) { 699 700 List<String> folderPaths = new ArrayList<String>(); 701 List<CmsResource> topFolders = new ArrayList<CmsResource>(); 702 Map<String, CmsResource> foldersByPath = new HashMap<String, CmsResource>(); 703 for (CmsResource folder : folders) { 704 folderPaths.add(folder.getRootPath()); 705 foldersByPath.put(folder.getRootPath(), folder); 706 } 707 Collections.sort(folderPaths); 708 Set<String> topFolderPaths = new HashSet<String>(folderPaths); 709 for (int i = 0; i < folderPaths.size(); i++) { 710 for (int j = i + 1; j < folderPaths.size(); j++) { 711 if (folderPaths.get(j).startsWith((folderPaths.get(i)))) { 712 topFolderPaths.remove(folderPaths.get(j)); 713 } else { 714 break; 715 } 716 } 717 } 718 for (String path : topFolderPaths) { 719 topFolders.add(foldersByPath.get(path)); 720 } 721 return topFolders; 722 } 723 724 /** 725 * Initializes the publish list, ensuring all internal lists are in the right order.<p> 726 */ 727 protected void initialize() { 728 729 if (m_folderList != null) { 730 // ensure folders are sorted starting with parent folders 731 Collections.sort(m_folderList, I_CmsResource.COMPARE_ROOT_PATH); 732 } 733 734 if (m_fileList != null) { 735 // ensure files are sorted starting with files in parent folders 736 Collections.sort(m_fileList, I_CmsResource.COMPARE_ROOT_PATH); 737 } 738 739 if (m_deletedFolderList != null) { 740 // ensure deleted folders are sorted starting with child folders 741 Collections.sort(m_deletedFolderList, I_CmsResource.COMPARE_ROOT_PATH); 742 Collections.reverse(m_deletedFolderList); 743 } 744 } 745 746 /** 747 * Removes a Cms resource from the publish list.<p> 748 * 749 * @param resource a Cms resource 750 * 751 * @return true if this publish list contains the specified resource 752 * 753 * @see List#remove(java.lang.Object) 754 */ 755 protected boolean remove(CmsResource resource) { 756 757 // it is essential that this method is only visible within the db package! 758 boolean ret = m_fileList.remove(resource); 759 ret |= m_folderList.remove(resource); 760 ret |= m_deletedFolderList.remove(resource); 761 return ret; 762 } 763 764 /** 765 * Builds a list of <code>CmsResource</code> instances from a list of resource structure IDs.<p> 766 * 767 * @param cms a cms object 768 * @param uuidList the list of structure IDs 769 * @return a list of <code>CmsResource</code> instances 770 */ 771 private List<CmsResource> internalReadResourceList(CmsObject cms, List<CmsUUID> uuidList) { 772 773 List<CmsResource> resList = new ArrayList<CmsResource>(uuidList.size()); 774 for (Iterator<CmsUUID> i = uuidList.iterator(); i.hasNext();) { 775 try { 776 CmsResource res = cms.readResource(i.next(), CmsResourceFilter.ALL); 777 resList.add(res); 778 } catch (CmsException exc) { 779 LOG.error(exc.getLocalizedMessage(), exc); 780 } 781 } 782 783 return resList; 784 } 785 786 /** 787 * Reads a UUID from an object input.<p> 788 * 789 * @param in the object input 790 * @return a UUID 791 * @throws IOException 792 */ 793 private CmsUUID internalReadUUID(ObjectInput in) throws IOException { 794 795 byte[] bytes = new byte[UUID_LENGTH]; 796 in.readFully(bytes, 0, UUID_LENGTH); 797 return new CmsUUID(bytes); 798 } 799 800 /** 801 * Reads a sequence of UUIDs from an object input and builds a list of <code>CmsResource</code> instances from it.<p> 802 * 803 * @param in the object input 804 * @return a list of <code>{@link CmsResource}</code> instances 805 * 806 * @throws IOException if something goes wrong 807 */ 808 private List<CmsUUID> internalReadUUIDList(ObjectInput in) throws IOException { 809 810 List<CmsUUID> result = null; 811 812 int i = in.readInt(); 813 if (i >= 0) { 814 result = new ArrayList<CmsUUID>(); 815 while (i > 0) { 816 result.add(internalReadUUID(in)); 817 i--; 818 } 819 } 820 821 return result; 822 } 823}