001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.changes; 020 021import java.io.InputStream; 022import java.util.Iterator; 023import java.util.LinkedHashSet; 024import java.util.Set; 025 026import org.apache.commons.compress.archivers.ArchiveEntry; 027 028/** 029 * ChangeSet collects and performs changes to an archive. 030 * Putting delete changes in this ChangeSet from multiple threads can 031 * cause conflicts. 032 * 033 * @NotThreadSafe 034 */ 035public final class ChangeSet { 036 037 private final Set<Change> changes = new LinkedHashSet<Change>(); 038 039 /** 040 * Deletes the file with the filename from the archive. 041 * 042 * @param filename 043 * the filename of the file to delete 044 */ 045 public void delete(final String filename) { 046 addDeletion(new Change(filename, Change.TYPE_DELETE)); 047 } 048 049 /** 050 * Deletes the directory tree from the archive. 051 * 052 * @param dirName 053 * the name of the directory tree to delete 054 */ 055 public void deleteDir(final String dirName) { 056 addDeletion(new Change(dirName, Change.TYPE_DELETE_DIR)); 057 } 058 059 /** 060 * Adds a new archive entry to the archive. 061 * 062 * @param pEntry 063 * the entry to add 064 * @param pInput 065 * the datastream to add 066 */ 067 public void add(final ArchiveEntry pEntry, final InputStream pInput) { 068 this.add(pEntry, pInput, true); 069 } 070 071 /** 072 * Adds a new archive entry to the archive. 073 * If replace is set to true, this change will replace all other additions 074 * done in this ChangeSet and all existing entries in the original stream. 075 * 076 * @param pEntry 077 * the entry to add 078 * @param pInput 079 * the datastream to add 080 * @param replace 081 * indicates the this change should replace existing entries 082 */ 083 public void add(final ArchiveEntry pEntry, final InputStream pInput, final boolean replace) { 084 addAddition(new Change(pEntry, pInput, replace)); 085 } 086 087 /** 088 * Adds an addition change. 089 * 090 * @param pChange 091 * the change which should result in an addition 092 */ 093 private void addAddition(Change pChange) { 094 if (Change.TYPE_ADD != pChange.type() || 095 pChange.getInput() == null) { 096 return; 097 } 098 099 if (!changes.isEmpty()) { 100 for (Iterator<Change> it = changes.iterator(); it.hasNext();) { 101 Change change = it.next(); 102 if (change.type() == Change.TYPE_ADD 103 && change.getEntry() != null) { 104 ArchiveEntry entry = change.getEntry(); 105 106 if(entry.equals(pChange.getEntry())) { 107 if(pChange.isReplaceMode()) { 108 it.remove(); 109 changes.add(pChange); 110 return; 111 } else { 112 // do not add this change 113 return; 114 } 115 } 116 } 117 } 118 } 119 changes.add(pChange); 120 } 121 122 /** 123 * Adds an delete change. 124 * 125 * @param pChange 126 * the change which should result in a deletion 127 */ 128 private void addDeletion(Change pChange) { 129 if ((Change.TYPE_DELETE != pChange.type() && 130 Change.TYPE_DELETE_DIR != pChange.type()) || 131 pChange.targetFile() == null) { 132 return; 133 } 134 String source = pChange.targetFile(); 135 136 if (source != null && !changes.isEmpty()) { 137 for (Iterator<Change> it = changes.iterator(); it.hasNext();) { 138 Change change = it.next(); 139 if (change.type() == Change.TYPE_ADD 140 && change.getEntry() != null) { 141 String target = change.getEntry().getName(); 142 143 if (target == null) { 144 continue; 145 } 146 147 if (Change.TYPE_DELETE == pChange.type() && source.equals(target)) { 148 it.remove(); 149 } else if (Change.TYPE_DELETE_DIR == pChange.type() && 150 target.matches(source + "/.*")) { 151 it.remove(); 152 } 153 } 154 } 155 } 156 changes.add(pChange); 157 } 158 159 /** 160 * Returns the list of changes as a copy. Changes on this set 161 * are not reflected on this ChangeSet and vice versa. 162 * @return the changes as a copy 163 */ 164 Set<Change> getChanges() { 165 return new LinkedHashSet<Change>(changes); 166 } 167}