diff --git a/.classpath b/.classpath index 46879d1..b3cf99e 100644 --- a/.classpath +++ b/.classpath @@ -1,10 +1,8 @@ - - + + - - - + diff --git a/.gitignore b/.gitignore index 7210f93..fe0ae97 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ velocity.log build bin junit-reports +lib +.DS_Store +.idea +*~ +*.iml diff --git a/.project b/.project index f8e40fd..306ecfe 100644 --- a/.project +++ b/.project @@ -1,17 +1,18 @@ - - - zipdiff - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + zipdiff + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.apache.ivyde.eclipse.ivynature + + diff --git a/.settings/edu.umd.cs.findbugs.core.prefs b/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 0000000..a57b5ff --- /dev/null +++ b/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,132 @@ +#FindBugs User Preferences +#Tue Apr 23 16:18:26 CEST 2013 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|false +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=2 +effort=default +filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false|15 +filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL| +run_at_full_build=false diff --git a/.settings/org.apache.ivyde.eclipse.prefs b/.settings/org.apache.ivyde.eclipse.prefs new file mode 100644 index 0000000..a95f744 --- /dev/null +++ b/.settings/org.apache.ivyde.eclipse.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.apache.ivyde.eclipse.standaloneretrieve= diff --git a/build.xml b/build.xml index 5da2a7c..08b585d 100644 --- a/build.xml +++ b/build.xml @@ -5,145 +5,226 @@ - - - - - + + - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + sourcepath="${java.src.dir}" + defaultexcludes="yes" + destdir="${javadoc.build.dir}" + author="true" + version="true" + use="true" + classpathref="project.class.path" + windowtitle="${name}"/> - - - + + + + + + + + + ${xdoc.html} + + + + + + + + + + + + + + + + + + manifest="${metadata.dir}/META-INF/JAR-manifest.txt" + compress="true"> + +
+ + +
+
+ includes="zipdiff/**/*.class" excludes="zipdiff/ant/*.class"/> +
+
+ + + + +
+ + +
+
+ + + + + + +
- - - + + + - - - - + + + + - - - + + + - - - - - - - - - - - - - + + + + + - - - - - - + + + + + - + - - + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - -
diff --git a/ivy.settings.xml b/ivy.settings.xml new file mode 100644 index 0000000..6188b51 --- /dev/null +++ b/ivy.settings.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/ivy.xml b/ivy.xml new file mode 100644 index 0000000..0158372 --- /dev/null +++ b/ivy.xml @@ -0,0 +1,47 @@ + + + + + Zipdiff - an archive diff tool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/ant.jar b/lib/ant.jar deleted file mode 100644 index cf01339..0000000 Binary files a/lib/ant.jar and /dev/null differ diff --git a/lib/commons-cli-1.0-LICENSE b/lib/commons-cli-1.0-LICENSE deleted file mode 100644 index a8684f1..0000000 --- a/lib/commons-cli-1.0-LICENSE +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Header: /home/cvs/jakarta-commons/LICENSE,v 1.4 2002/04/11 13:24:02 dion Exp $ - * $Revision: 1.4 $ - * $Date: 2002/04/11 13:24:02 $ - * - * ==================================================================== - * - * The Apache Software License, Version 1.1 - * - * Copyright (c) 1999-2001 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ diff --git a/lib/commons-cli-1.0.jar b/lib/commons-cli-1.0.jar deleted file mode 100644 index 22a004e..0000000 Binary files a/lib/commons-cli-1.0.jar and /dev/null differ diff --git a/lib/junit.jar b/lib/junit.jar deleted file mode 100644 index 674d71e..0000000 Binary files a/lib/junit.jar and /dev/null differ diff --git a/project.properties b/maven/project.properties similarity index 100% rename from project.properties rename to maven/project.properties diff --git a/project.xml b/maven/project.xml similarity index 100% rename from project.xml rename to maven/project.xml diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..477467e --- /dev/null +++ b/readme.md @@ -0,0 +1,29 @@ +ZipDiff compares two .zip (.jar, .war, .ear, .rar) files and creates a list of differences. +Plain text, .xml, .html and even a .zip file are supported as output formats. + +ZipDiff can be executed as command line tool or Ant task. + + +Command line arguments +---------------------- + + java -jar zipdiff.jar --source foo.zip --target bar.zip [options] + +Valid options are: + +* `--comparecrcvalues` compares the CRC values in addition to file size +* `--comparetimestamps` compares timestamps in addition to file size +* `--excluderegex ` excludes file names matching regex from comparison +* `--excludescmfiles` excludes SCM folders from comparison (SCCS, RCS, CVS, .svn, .bzr, .hg, .git) +* `--output ` name of the output file +* `--trimoutputlevels ` number of path segments to trim in the output file +* `--trimsourcelevels ` number of path segments to trim in the source file +* `--trimtargetlevels ` number of path segments to trim in the target file +* `--errorondifference` use "error" return code (2) if differences have been detected rather than 1 +* `--verbose` print detailed messages + +This version can be found at https://github.com/nhnb/zipdiff + +The original zipdiff project was developed by Sean C. Sullivan and James Stewart at http://zipdiff.sourceforge.net/ + +License: see LICENSE.txt diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 81b321c..0000000 --- a/readme.txt +++ /dev/null @@ -1,30 +0,0 @@ -ZipDiff compares two .zip (.jar, .war, .jar) files and creates a list of differences. Plain text, .xml, .html and even a .zip file are supported as output formats. - -ZipDiff can be executed as command line tool or ant task. - - -Command line arguments ----------------------- - -java -jar zipdiff.jar -file1 foo.zip -file2 bar.zip [ options] - -Valid options are: - ---comparecrcvalues compares the crc values instead of the file content ---comparetimestamps compares timestamps instead of file content ---ignorecvsfiles ignores differences in CVS folders ---outputfile name of the output file ---skipoutputprefixes n number of path segment to skip in the output file ---skipprefixes1 n number of path segment to skip in the first file ---skipprefixes2 n number of path segment to skip in the second file ---exitwitherrorondifference use an error code other than 0, if differences have been detected ---verbose print detail messages - - - -This version can be found at https://github.com/nhnb/zipdiff - - -The original zipdiff project was developed by Sean C. Sullivan and James Stewart at http://zipdiff.sourceforge.net/ - -License: see LICENSE.txt diff --git a/resources/logging.properties b/resources/logging.properties deleted file mode 100755 index 41b19fb..0000000 --- a/resources/logging.properties +++ /dev/null @@ -1,12 +0,0 @@ -# run with -Djava.util.logging.config.file=resources/logging.properties - -handlers= java.util.logging.ConsoleHandler - -.level = FINEST - -java.util.logging.ConsoleHandler.level=FINEST - -java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter - -zipdiff.DifferenceCalculator.level=FINER - diff --git a/src/main/java/zipdiff/DifferenceCalculator.java b/src/main/java/zipdiff/DifferenceCalculator.java new file mode 100644 index 0000000..84e3b92 --- /dev/null +++ b/src/main/java/zipdiff/DifferenceCalculator.java @@ -0,0 +1,518 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +import zipdiff.util.StringUtil; + +/** + * Checks and compiles differences between two zip files. + * It also has the ability to exclude entries from the comparison + * based on a regular expression. + * + * @author Sean C. Sullivan, Hendrik Brummermann + */ +public class DifferenceCalculator { + + /** + * Field logger. + */ + private final Logger logger = Logger.getLogger(getClass().getName()); + + /** + * Field source. + */ + private final ZipFile source; + + /** + * Field target. + */ + private final ZipFile target; + + /** + * Field numberOfSourceLevelsToTrim. + */ + private int numberOfSourceLevelsToTrim = 0; + + /** + * Field numberOfTargetLevelsToTrim. + */ + private int numberOfTargetLevelsToTrim = 0; + + /** + * Field compareTimestamps. + */ + private boolean compareTimestamps = false; + + /** + * Field excludeSCMFiles. + */ + private boolean excludeSCMFiles = false; + + /** + * Field compareCRCValues. + */ + private boolean compareCRCValues = true; + + /** + * Field excludePattern. + */ + private Pattern excludePattern; + + /** + * Field bVerbose. + */ + private boolean bVerbose = false; + + /** + * Constructor taking 2 filenames to compare + * @param source String + * @param target String + * @throws IOException + */ + public DifferenceCalculator(String source, String target) throws IOException { + this(new File(source), new File(target)); + } + + /** + * Constructor taking 2 Files to compare + * @param sourcefile File + * @param targetfile File + * @throws IOException + */ + public DifferenceCalculator(File sourcefile, File targetfile) throws IOException { + this(new ZipFile(sourcefile), new ZipFile(targetfile)); + } + + /** + * Constructor taking 2 ZipFiles to compare + * @param sourcezip ZipFile + * @param targetzip ZipFile + */ + public DifferenceCalculator(ZipFile sourcezip, ZipFile targetzip) { + source = sourcezip; + target = targetzip; + } + + /** + * Method debug. + * @param msg Object + */ + protected void debug(Object msg) { + if (isVerbose()) { + System.out.println("[" + DifferenceCalculator.class.getName() + "] " + String.valueOf(msg)); + } + } + + /** + * Set the verboseness of the debug output. + * @param b true to make verbose + */ + public void setVerbose(boolean b) { + bVerbose = b; + } + + /** + * Method isVerboseEnabled. + * @return boolean + */ + protected boolean isVerbose() { + return bVerbose; + } + + /** + * Parses Regex that excludes matching ZipEntry + * @param patterns A Set of regular expressions that exclude a ZipEntry from comparison if matched. + * @see java.util.regex.Pattern + */ + public void setExcludeRegex(Set patterns) { + if (patterns == null) { + excludePattern = null; + } else if (patterns.isEmpty()) { + excludePattern = null; + } else { + String regex = ""; + + for (String pattern: patterns) { + regex += (regex.isEmpty()) ? String.format("(%s)", pattern) : String.format("|(%s)", pattern); + } + excludePattern = Pattern.compile(regex); + logger.log(Level.FINE, "Regular expression is : " + regex); + } + } + + /** + * Returns true if excludePattern matches the given entryName + * @param filepath ditto + * @param entryName The name of ZipEntry to be checked if it should be excluded. + * @return true if the ZipEntry should be excluded. + */ + protected boolean excludeThisFile(String filepath, String entryName) { + if (entryName == null) { + return false; + } + + if (isSCMFile(filepath, entryName) && isExcludingSCMFiles()) { + return true; + } + + if (excludePattern == null) { + return false; + } + + final Matcher matcher = excludePattern.matcher(entryName); + final boolean match = matcher.matches(); + if (match) { + logger.log(Level.FINEST, "Found a match against : " + entryName + " so excluding"); + } + return match; + } + + /** + * Method isSCMFile. + * @param filepath String + * @param entryName String + * @return boolean + */ + protected boolean isSCMFile(String filepath, String entryName) { + return entryName != null + && (filepath.contains("CVS/") || entryName.contains("CVS/") + || filepath.contains("RCS/") || entryName.contains("RCS/") + || filepath.contains("SCCS/") || entryName.contains("SCCS/") + || filepath.contains(".svn/") || entryName.contains(".svn/") + || filepath.contains(".bzr/") || entryName.contains(".bzr/") + || filepath.contains(".hg/") || entryName.contains(".hg/") + || filepath.contains(".git/") || entryName.contains(".git/")); + } + + /** + * Ensure that the comparison checks against the CRCs of the entries. + * @param b true ensures that CRCs will be checked + */ + public void setCompareCRCValues(boolean b) { + compareCRCValues = b; + } + + /** + * Returns value of CRC check flag + * @return true if this instance will check the CRCs of each ZipEntry + */ + public boolean isComparingCRCValues() { + return compareCRCValues; + } + + /** + * Method setCompareTimestamps. + * @param b boolean + */ + public void setCompareTimestamps(boolean b) { + compareTimestamps = b; + } + + /** + * Method isComparingTimestamps. + * @return boolean + */ + public boolean isComparingTimestamps() { + return compareTimestamps; + } + + /** + * Method isExcludingSCMFiles. + * @return boolean + */ + public boolean isExcludingSCMFiles() { + return excludeSCMFiles; + } + + /** + * Method setExcludeSCMFiles. + * @param b boolean + */ + public void setExcludeSCMFiles(boolean b) { + excludeSCMFiles = b; + } + + /** + * Sets the number of directory levels to trim in the source file + * @param numberOfLevelsToTrim number of directory levels to trim + */ + public void setNumberOfSourceLevelsToTrim(int numberOfLevelsToTrim) { + this.numberOfSourceLevelsToTrim = numberOfLevelsToTrim; + } + + /** + * Gets the number of directory levels to trim in the source file + * @return number of directory levels to trim + */ + public int getNumberOfSourceLevelsToTrim() { + return numberOfSourceLevelsToTrim; + } + + /** + * Sets the number of directory levels to trim in the target file + * @param numberOfLevelsToTrim number of directory levels to trim + */ + public void setNumberOfTargetLevelsToTrim(int numberOfLevelsToTrim) { + this.numberOfTargetLevelsToTrim = numberOfLevelsToTrim; + } + + /** + * Gets the number of directory levels to trim in the target file + * @return number of directory levels to trim + */ + public int getNumberOfTargetLevelsToTrim() { + return numberOfTargetLevelsToTrim; + } + + /** + * Opens the ZipFile and builds up a map of all the entries. The key is the name of + * the entry and the value is the ZipEntry itself. + * @param zf The ZipFile for which to build up the map of ZipEntries + * @return The map containing all the ZipEntries. The key being the name of the ZipEntry. + * @throws IOException + */ + protected Map buildZipEntryMap(ZipFile zf) throws IOException { + return buildZipEntryMap(zf, 0); + } + + /** + * Opens the ZipFile and builds up a map of all the entries. The key is the name of + * the entry and the value is the ZipEntry itself. + * @param zf The ZipFile for which to build up the map of ZipEntries + * @param nl Number of directory levels to trim + * @return The map containing all the ZipEntries. The key being the name of the ZipEntry. + * @throws IOException + */ + protected Map buildZipEntryMap(ZipFile zf, int nl) throws IOException { + final Map zipEntryMap = new HashMap(); + try { + final Enumeration entries = zf.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + InputStream is = null; + try { + is = zf.getInputStream(entry); + processZipEntry("", entry, is, zipEntryMap, nl); + } finally { + if (is != null) { + is.close(); + } + } + } + } finally { + zf.close(); + } + + return zipEntryMap; + } + + /** + * Will place ZipEntries for a given ZipEntry into the given Map. More ZipEntries will result + * if zipEntry is itself a ZipFile. All embedded ZipFiles will be processed with their names + * prefixed onto the names of their ZipEntries. + * @param prefix The prefix of the ZipEntry that should be added to the key. Typically used + * when processing embedded ZipFiles. The name of the embedded ZipFile would be the prefix of + * all the embedded ZipEntries. + * @param zipEntry The ZipEntry to place into the Map. If it is a ZipFile then all its ZipEntries + * will also be placed in the Map. + * @param is The InputStream of the corresponding ZipEntry. + * @param zipEntryMap The Map in which to place all the ZipEntries into. The key will + * be the name of the ZipEntry. + * @throws IOException + */ + protected void processZipEntry(String prefix, ZipEntry zipEntry, InputStream is, Map zipEntryMap) throws IOException { + processZipEntry(prefix, zipEntry, is, zipEntryMap, 0); + } + + /** + * Will place ZipEntries for a given ZipEntry into the given Map. More ZipEntries will result + * if zipEntry is itself a ZipFile. All embedded ZipFiles will be processed with their names + * prefixed onto the names of their ZipEntries. + * @param prefix The prefix of the ZipEntry that should be added to the key. Typically used + * when processing embedded ZipFiles. The name of the embedded ZipFile would be the prefix of + * all the embedded ZipEntries. + * @param zipEntry The ZipEntry to place into the Map. If it is a ZipFile then all its ZipEntries + * will also be placed in the Map. + * @param is The InputStream of the corresponding ZipEntry. + * @param zipEntryMap The Map in which to place all the ZipEntries into. The key will + * be the name of the ZipEntry. + * @param nl Number of directory levels to trim + * @throws IOException + */ + protected void processZipEntry(String prefix, ZipEntry zipEntry, InputStream is, Map zipEntryMap, int nl) throws IOException { + if (excludeThisFile(prefix, zipEntry.getName())) { + logger.log(Level.FINE, "ignoring file: " + zipEntry.getName()); + } else { + final String name = StringUtil.removeDirectoryPrefix(prefix + zipEntry.getName(), nl); + if ((name == null) || name.equals("")) { + return; + } + + logger.log(Level.FINEST, "processing ZipEntry: " + name); + zipEntryMap.put(name, zipEntry); + + if (!zipEntry.isDirectory() && isZipFile(name)) { + processEmbeddedZipFile(name + "!", is, zipEntryMap); + } + } + } + + /** + * Method processEmbeddedZipFile. + * @param prefix String + * @param is InputStream + * @param m Map<String,ZipEntry> + * @throws IOException + */ + protected void processEmbeddedZipFile(String prefix, InputStream is, Map m) throws IOException { + final ZipInputStream zis = new ZipInputStream(is); + + ZipEntry entry = zis.getNextEntry(); + + while (entry != null) { + processZipEntry(prefix, entry, zis, m); + zis.closeEntry(); + entry = zis.getNextEntry(); + } + } + + /** + * Returns true if the filename has a valid zip extension (i.e. jar, war, ear, rar, zip) + * @param filename The name of the file to check. + * @return true if it has a valid extension. + */ + @SuppressWarnings("resource") + public static boolean isZipFile(String filename) { + try { + return new ZipInputStream(new FileInputStream(filename)).getNextEntry() != null; + } catch (IOException e) { + return false; + } + } + + /** + * Calculates all the differences between two zip files. + * It builds up the 2 maps of ZipEntries for the two files + * and then compares them. + * @param sourcezip The source ZipFile to compare + * @param targetzip The target ZipFile to compare + * @return All the differences between the two files. + * @throws IOException + */ + protected Differences calculateDifferences(ZipFile sourcezip, ZipFile targetzip) throws IOException { + return calculateDifferences(sourcezip, targetzip, 0, 0); + } + + /** + * Calculates all the differences between two zip files. + * It builds up the 2 maps of ZipEntries for the two files + * and then compares them. + * @param sourcezip The source ZipFile to compare + * @param targetzip The target ZipFile to compare + * @param nsourcel number of directory levels to trim in the source file + * @param ntargetl number of directory levels to trim in the target file + * @return All the differences between the two files. + * @throws IOException + */ + protected Differences calculateDifferences(ZipFile sourcezip, ZipFile targetzip, int nsourcel, int ntargetl) throws IOException { + final Map sourcemap = buildZipEntryMap(sourcezip, nsourcel); + final Map targetmap = buildZipEntryMap(targetzip, ntargetl); + + return calculateDifferences(sourcemap, targetmap); + } + + /** + * Given two Maps of ZipEntries it will generate a Differences of all the + * differences found between the two maps. + * @param sourcemap Map<String, ZipEntry> + * @param targetmap Map<String, ZipEntry> + * @return All the differences found between the two maps + */ + protected Differences calculateDifferences(Map sourcemap, Map targetmap) { + final Differences diff = new Differences(); + + final Set sourcenames = sourcemap.keySet(); + final Set targetnames = targetmap.keySet(); + + final Set allNames = new HashSet(); + allNames.addAll(sourcenames); + allNames.addAll(targetnames); + + for (String name: allNames) { + if (excludeThisFile("", name)) { + // do nothing + } else if (sourcenames.contains(name) && (!targetnames.contains(name))) { + diff.fileRemoved(name, sourcemap.get(name)); + } else if (targetnames.contains(name) && (!sourcenames.contains(name))) { + diff.fileAdded(name, targetmap.get(name)); + } else if (sourcenames.contains(name) && (targetnames.contains(name))) { + ZipEntry srcentry = sourcemap.get(name); + ZipEntry trgentry = targetmap.get(name); + if (!entriesMatch(srcentry, trgentry)) { + diff.fileChanged(name, srcentry, trgentry); + } + } else { + throw new IllegalStateException("unexpected state"); + } + } + + return diff; + } + + /** + * returns true if the two entries are equivalent in type, name, size, compressed size + * and time or CRC. + * @param srcentry The source ZipEntry to compare + * @param trgentry The target ZipEntry to compare + * @return true if the entries are equivalent. + */ + protected boolean entriesMatch(ZipEntry srcentry, ZipEntry trgentry) { + boolean result = + (srcentry.isDirectory() == trgentry.isDirectory()) + && (srcentry.getSize() == trgentry.getSize()) + && (srcentry.getCompressedSize() == trgentry.getCompressedSize()); + + if (isComparingTimestamps()) { + result = result && (srcentry.getTime() == trgentry.getTime()); + } + + if (isComparingCRCValues()) { + result = result && (srcentry.getCrc() == trgentry.getCrc()); + } + + return result; + } + + /** + * Calculates differences between source and target files and saves their names + * @return all the differences found between the two zip files. + * @throws IOException + */ + public Differences getDifferences() throws IOException { + final Differences diff = calculateDifferences(source, target, numberOfSourceLevelsToTrim, numberOfTargetLevelsToTrim); + diff.setSource(source.getName()); + diff.setTarget(target.getName()); + + return diff; + } +} diff --git a/src/main/java/zipdiff/Differences.java b/src/main/java/zipdiff/Differences.java new file mode 100644 index 0000000..ada3c88 --- /dev/null +++ b/src/main/java/zipdiff/Differences.java @@ -0,0 +1,191 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff; + +import java.util.Map; +import java.util.TreeMap; +import java.util.zip.ZipEntry; + +/** + * Used to keep track of difference between 2 zip files. + * + * @author Sean C. Sullivan + */ +public class Differences { + /** + * Field added. + */ + private final Map added = new TreeMap(); + + /** + * Field removed. + */ + private final Map removed = new TreeMap(); + + /** + * Field changed. + */ + private final Map changed = new TreeMap(); + + /** + * Field excluded. + */ + private final Map excluded = new TreeMap(); + + /** + * Field source. + */ + private String source; + + /** + * Field target. + */ + private String target; + + /** + * Constructor for Differences. + */ + public Differences() { + // todo + } + + /** + * Method setSource. + * @param filename String + */ + public void setSource(String filename) { + this.source = filename; + } + + /** + * Method setTarget. + * @param filename String + */ + public void setTarget(String filename) { + this.target = filename; + } + + /** + * Method getSource. + * @return String + */ + public String getSource() { + return this.source; + } + + /** + * Method getTarget. + * @return String + */ + public String getTarget() { + return this.target; + } + + /** + * Method fileAdded. + * @param fqn String + * @param ze ZipEntry + */ + public void fileAdded(String fqn, ZipEntry ze) { + this.added.put(fqn, new ZipEntry[] {ze}); + } + + /** + * Method fileRemoved. + * @param fqn String + * @param ze ZipEntry + */ + public void fileRemoved(String fqn, ZipEntry ze) { + this.removed.put(fqn, new ZipEntry[] {ze}); + } + + /** + * Method fileExcluded. + * @param fqn String + * @param ze ZipEntry + */ + public void fileExcluded(String fqn, ZipEntry ze) { + this.excluded.put(fqn, new ZipEntry[] {ze}); + } + + /** + * Method fileChanged. + * @param fqn String + * @param srcze ZipEntry + * @param trgze ZipEntry + */ + public void fileChanged(String fqn, ZipEntry srcze, ZipEntry trgze) { + this.changed.put(fqn, new ZipEntry[] {srcze, trgze}); + } + + /** + * Method getAdded. + * @return Map<String, ZipEntry[]> + */ + public Map getAdded() { + return this.added; + } + + /** + * Method getRemoved. + * @return Map<String, ZipEntry[]> + */ + public Map getRemoved() { + return this.removed; + } + + /** + * Method getChanged. + * @return Map<String, ZipEntry[]> + */ + public Map getChanged() { + return this.changed; + } + + /** + * Method getExcluded. + * @return Map<String, ZipEntry[]> + */ + public Map getExcluded() { + return this.excluded; + } + + /** + * Method hasDifferences. + * @return boolean + */ + public boolean hasDifferences() { + return (getChanged().size() > 0) || (getAdded().size() > 0) || (getRemoved().size() > 0); + } + + /** + * Method toString. + * @return String + */ + public String toString() { + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format("%d file%s added to %s\n", getAdded().size(), (getAdded().size() == 1) ? "" : "s", getTarget())); + for (String key : getAdded().keySet()) { + sb.append(String.format("\t[added] %s\n", key)); + } + + sb.append(String.format("%d file%s removed from %s\n", getRemoved().size(), (getRemoved().size() == 1) ? "" : "s", getSource())); + for (String key : getRemoved().keySet()) { + sb.append(String.format("\t[removed] %s\n", key)); + } + + sb.append(String.format("%d file%s changed\n", getChanged().size(), (getChanged().size() == 1) ? "" : "s")); + for (String name : getChanged().keySet()) { + ZipEntry[] entries = getChanged().get(name); + sb.append(String.format("\t[changed] %s (size: %d : %d)\n", name, entries[0].getSize(), entries[1].getSize())); + } + + final int differenceCount = getAdded().size() + getChanged().size() + getRemoved().size(); + sb.append(String.format("Total differences: %d", differenceCount)); + return sb.toString(); + } +} diff --git a/src/main/java/zipdiff/Main.java b/src/main/java/zipdiff/Main.java new file mode 100644 index 0000000..3a3c39c --- /dev/null +++ b/src/main/java/zipdiff/Main.java @@ -0,0 +1,333 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.MissingOptionException; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import zipdiff.output.Builder; +import zipdiff.output.BuilderFactory; + +/** + * Provides a command line interface to zipdiff + * + * @author Sean C. Sullivan, J.Stewart, Hendrik Brummermann + */ +public class Main { + /** + * Field EXITCODE_ERROR. + * (value is 2) + */ + private static final int EXITCODE_ERROR = 2; + + /** + * Field EXITCODE_DIFF. + * (value is 1) + */ + private static final int EXITCODE_DIFF = 1; + + /** + * Field OPTION_COMPARE_CRC_VALUES. + * (value is ""comparecrcvalues"") + */ + private static final String OPTION_COMPARE_CRC_VALUES = "comparecrcvalues"; + + /** + * Field OPTION_COMPARE_TIMESTAMPS. + * (value is ""comparetimestamps"") + */ + private static final String OPTION_COMPARE_TIMESTAMPS = "comparetimestamps"; + + /** + * Field OPTION_EXCLUDE_SCM_FILES. + * (value is ""excludescmfiles"") + */ + private static final String OPTION_EXCLUDE_SCM_FILES = "excludescmfiles"; + + /** + * Field OPTION_OUTPUT_FILE. + * (value is ""output"") + */ + private static final String OPTION_OUTPUT_FILE = "output"; + + /** + * Field OPTION_SOURCE_FILE. + * (value is ""source"") + */ + private static final String OPTION_SOURCE_FILE = "source"; + + /** + * Field OPTION_TARGET_FILE. + * (value is ""target"") + */ + private static final String OPTION_TARGET_FILE = "target"; + + /** + * Field OPTION_TRIM_OUTPUT_LEVELS. + * (value is ""trimoutputlevels"") + */ + private static final String OPTION_TRIM_OUTPUT_LEVELS = "trimoutputlevels"; + + /** + * Field OPTION_TRIM_SOURCE_LEVELS. + * (value is ""trimsourcelevels"") + */ + private static final String OPTION_TRIM_SOURCE_LEVELS = "trimsourcelevels"; + + /** + * Field OPTION_TRIM_TARGET_LEVELS. + * (value is ""trimtargetlevels"") + */ + private static final String OPTION_TRIM_TARGET_LEVELS = "trimtargetlevels"; + + /** + * Field OPTION_EXCLUDE_REGEX. + * (value is ""excluderegex"") + */ + private static final String OPTION_EXCLUDE_REGEX = "excluderegex"; + + /** + * Field OPTION_ERROR_ON_DIFF. + * (value is ""errorondifference"") + */ + private static final String OPTION_ERROR_ON_DIFF = "errorondifference"; + + /** + * Field OPTION_VERBOSE. + * (value is ""verbose"") + */ + private static final String OPTION_VERBOSE = "verbose"; + + /** + * Field OPTIONS. + */ + private static final Options OPTIONS; + + // static initializer + static { + OPTIONS = new Options(); + + final Option compareTS = + new Option(OPTION_COMPARE_TIMESTAMPS, OPTION_COMPARE_TIMESTAMPS, false, "compare timestamps"); + compareTS.setRequired(false); + + final Option compareCRC = + new Option(OPTION_COMPARE_CRC_VALUES, OPTION_COMPARE_CRC_VALUES, false, "compare CRC values"); + compareCRC.setRequired(false); + + final Option source = + new Option(OPTION_SOURCE_FILE, OPTION_SOURCE_FILE, true, "source file to compare"); + source.setRequired(true); + + final Option target = + new Option(OPTION_TARGET_FILE, OPTION_TARGET_FILE, true, "target file to compare"); + target.setRequired(true); + + final Option numberOfLevelsToTrimInOutput = + new Option(OPTION_TRIM_OUTPUT_LEVELS, OPTION_TRIM_OUTPUT_LEVELS, true, + "number of directory levels to trim in the output file (if supported by output processor"); + numberOfLevelsToTrimInOutput.setRequired(false); + + final Option numberOfLevelsToTrimInSource = + new Option(OPTION_TRIM_SOURCE_LEVELS, OPTION_TRIM_SOURCE_LEVELS, true, + "number of directory levels to trim in the source file"); + numberOfLevelsToTrimInSource.setRequired(false); + + final Option numberOfLevelsToTrimInTarget = + new Option(OPTION_TRIM_TARGET_LEVELS, OPTION_TRIM_TARGET_LEVELS, true, + "number of directory levels to trim in the target file"); + numberOfLevelsToTrimInTarget.setRequired(false); + + final Option outputFileOption = + new Option(OPTION_OUTPUT_FILE, OPTION_OUTPUT_FILE, true, "output filename"); + outputFileOption.setRequired(false); + + final Option regex = + new Option(OPTION_EXCLUDE_REGEX, OPTION_EXCLUDE_REGEX, true, + "regular expression to exclude matching files e.g. (?i)meta-inf.*"); + regex.setRequired(false); + + final Option excludeSCMFilesOption = + new Option(OPTION_EXCLUDE_SCM_FILES, OPTION_EXCLUDE_SCM_FILES, false, "exclude SCM files"); + excludeSCMFilesOption.setRequired(false); + + final Option exitWithError = + new Option(OPTION_ERROR_ON_DIFF, OPTION_ERROR_ON_DIFF, false, + "exit with error code " + EXITCODE_DIFF + " if a difference is found"); + + final Option verboseOption = + new Option(OPTION_VERBOSE, OPTION_VERBOSE, false, "verbose mode"); + + OPTIONS.addOption(compareTS); + OPTIONS.addOption(compareCRC); + OPTIONS.addOption(source); + OPTIONS.addOption(target); + OPTIONS.addOption(numberOfLevelsToTrimInOutput); + OPTIONS.addOption(numberOfLevelsToTrimInSource); + OPTIONS.addOption(numberOfLevelsToTrimInTarget); + OPTIONS.addOption(regex); + OPTIONS.addOption(excludeSCMFilesOption); + OPTIONS.addOption(exitWithError); + OPTIONS.addOption(verboseOption); + OPTIONS.addOption(outputFileOption); + } + + /** + * Method checkFile. + * @param f File + */ + private static void checkFile(File f) { + final String filename = f.toString(); + + if (!f.exists()) { + System.err.println("'" + filename + "' does not exist"); + System.exit(EXITCODE_ERROR); + } + + if (!f.canRead()) { + System.err.println("'" + filename + "' is not readable"); + System.exit(EXITCODE_ERROR); + } + + if (f.isDirectory()) { + System.err.println("'" + filename + "' is a directory"); + System.exit(EXITCODE_ERROR); + } + } + + /** + * Method writeOutputFile. + * @param filename String + * @param numberOfOutputLevelsToTrim int + * @param d Differences + * @throws IOException + */ + private static void writeOutputFile(String filename, int numberOfOutputLevelsToTrim, Differences d) throws IOException { + final Builder builder = BuilderFactory.create(filename); + builder.build(filename, numberOfOutputLevelsToTrim, d); + } + + /** + * The command line interface to zipdiff utility + * @param args The command line parameters + */ + public static void main(String[] args) { + final CommandLineParser parser = new GnuParser(); + + try { + final CommandLine line = parser.parse(OPTIONS, args); + + final String sourcefile = line.getOptionValue(OPTION_SOURCE_FILE); + final String targetfile = line.getOptionValue(OPTION_TARGET_FILE); + + final File source = new File(sourcefile); + final File target = new File(targetfile); + + checkFile(source); + checkFile(target); + + System.out.println("Source = " + source); + System.out.println("Target = " + target); + + final DifferenceCalculator calc = new DifferenceCalculator(source, target); + + int numberOfLevelsToTrimInSource = 0; + if (line.getOptionValue(OPTION_TRIM_SOURCE_LEVELS) != null) { + numberOfLevelsToTrimInSource = Integer.parseInt(line.getOptionValue(OPTION_TRIM_SOURCE_LEVELS)); + } + int numberOfLevelsToTrimInTarget = 0; + if (line.getOptionValue(OPTION_TRIM_TARGET_LEVELS) != null) { + numberOfLevelsToTrimInTarget = Integer.parseInt(line.getOptionValue(OPTION_TRIM_TARGET_LEVELS)); + } + int numberOfLevelsToTrimInOutput = 0; + if (line.getOptionValue(OPTION_TRIM_OUTPUT_LEVELS) != null) { + numberOfLevelsToTrimInOutput = Integer.parseInt(line.getOptionValue(OPTION_TRIM_OUTPUT_LEVELS)); + } + + calc.setNumberOfSourceLevelsToTrim(numberOfLevelsToTrimInSource); + calc.setNumberOfTargetLevelsToTrim(numberOfLevelsToTrimInTarget); + + if (line.hasOption(OPTION_COMPARE_CRC_VALUES)) { + calc.setCompareCRCValues(true); + } else { + calc.setCompareCRCValues(false); + } + + if (line.hasOption(OPTION_EXCLUDE_SCM_FILES)) { + calc.setExcludeSCMFiles(true); + } else { + calc.setExcludeSCMFiles(false); + } + + if (line.hasOption(OPTION_COMPARE_TIMESTAMPS)) { + calc.setCompareTimestamps(true); + } else { + calc.setCompareTimestamps(false); + } + + if (line.hasOption(OPTION_EXCLUDE_REGEX)) { + final String regularExpression = line.getOptionValue(OPTION_EXCLUDE_REGEX); + final Set regexSet = new HashSet(); + regexSet.add(regularExpression); + + calc.setExcludeRegex(regexSet); + } + + boolean exitWithErrorOnDiff = false; + if (line.hasOption(OPTION_ERROR_ON_DIFF)) { + exitWithErrorOnDiff = true; + } + + final Differences diff = calc.getDifferences(); + + if (line.hasOption(OPTION_OUTPUT_FILE)) { + final String outputfile = line.getOptionValue(OPTION_OUTPUT_FILE); + writeOutputFile(outputfile, numberOfLevelsToTrimInOutput, diff); + } + + if (diff.hasDifferences()) { + if (line.hasOption(OPTION_VERBOSE)) { + System.out.println(diff); + System.out.println(diff.getSource() + " and " + diff.getTarget() + " are different."); + } + if (exitWithErrorOnDiff) { + System.exit(EXITCODE_DIFF); + } + } else { + System.out.println("No differences found."); + } + } catch (MissingOptionException mox) { + final StringBuilder sb = new StringBuilder("Missing required options: "); + for (Object option : mox.getMissingOptions()) { + sb.append((String)option).append(", "); + } + sb.setLength(sb.length() - 2); + System.err.println(sb.toString()); + final HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("zipdiff.Main [options] ", OPTIONS); + System.exit(EXITCODE_ERROR); + } catch (ParseException pex) { + System.err.println(pex.getMessage()); + final HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("zipdiff.Main [options] ", OPTIONS); + System.exit(EXITCODE_ERROR); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(EXITCODE_ERROR); + } + } +} diff --git a/src/main/java/zipdiff/ant/ZipDiffTask.java b/src/main/java/zipdiff/ant/ZipDiffTask.java new file mode 100644 index 0000000..cbdac65 --- /dev/null +++ b/src/main/java/zipdiff/ant/ZipDiffTask.java @@ -0,0 +1,347 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff.ant; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +import zipdiff.DifferenceCalculator; +import zipdiff.Differences; +import zipdiff.output.Builder; +import zipdiff.output.BuilderFactory; + +/** + * Ant task for running zipdiff from a build.xml file + * + * @author Sean C. Sullivan + */ +public class ZipDiffTask extends Task { + /** + * Field source. + */ + private String source; + + /** + * Field target. + */ + private String target; + + /** + * Field numberOfOutputLevelsToTrim. + */ + private int numberOfOutputLevelsToTrim = 0; + + /** + * Field numberOfSourceLevelsToTrim. + */ + private int numberOfSourceLevelsToTrim = 0; + + /** + * Field numberOfTargetLevelsToTrim. + */ + private int numberOfTargetLevelsToTrim = 0; + + /** + * Field output. + */ + private String output = "-"; + + /** + * Field compareTimestamps. + */ + private boolean compareTimestamps = false; + + /** + * Field excludeSCMFiles. + */ + private boolean excludeSCMFiles = false; + + /** + * Field compareCRCValues. + */ + private boolean compareCRCValues = false; + + /** + * Field patterns. + */ + private final Set patterns = new HashSet(); + + /** + * Field property. + */ + private String property = ""; + + // Backwards compatibility stuff + + public void setFilename1(String name) { + log("!! Attribute filename1 is deprecated. !!", Project.MSG_WARN); + log("!! Use the source attribute instead. !!", Project.MSG_WARN); + this.source = name; + } + + public void setFilename2(String name) { + log("!! Attribute filename2 is deprecated. !!", Project.MSG_WARN); + log("!! Use the target attribute instead. !!", Project.MSG_WARN); + this.target = name; + } + + public void setDestFile(String name) { + log("!! Attribute destfile is deprecated. !!", Project.MSG_WARN); + log("!! Use the output attribute instead. !!", Project.MSG_WARN); + this.output = name; + } + + public void setIgnoreTimestamps(Boolean b) { + log("!! Attribute ignoretimestamps is deprecated. !!", Project.MSG_WARN); + log("!! Use the comparetimestamps attribute instead. !!", Project.MSG_WARN); + this.compareTimestamps = !b; + } + + /** + * Method setSource. + * @param name String + */ + public void setSource(String name) { + this.source = name; + } + + /** + * Method setTarget. + * @param name String + */ + public void setTarget(String name) { + this.target = name; + } + + /** + * Method getTrimOutputLevels. + * @return int + */ + public int getTrimOutputLevels() { + return this.numberOfOutputLevelsToTrim; + } + + /** + * Method setTrimOutputLevels. + * @param numberOfLevelsToTrim int + */ + public void setTrimOutputLevels(int numberOfLevelsToTrim) { + this.numberOfOutputLevelsToTrim = numberOfLevelsToTrim; + } + + /** + * Method getTrimSourceLevels. + * @return int + */ + public int getTrimSourceLevels() { + return this.numberOfSourceLevelsToTrim; + } + + /** + * Method setTrimSourceLevels. + * @param numberOfLevelsToTrim int + */ + public void setTrimSourceLevels(int numberOfLevelsToTrim) { + this.numberOfSourceLevelsToTrim = numberOfLevelsToTrim; + } + + /** + * Method getTrimTargetLevels. + * @return int + */ + public int getTrimTargetLevels() { + return this.numberOfTargetLevelsToTrim; + } + + /** + * Method setTrimTargetLevels. + * @param numberOfLevelsToTrim int + */ + public void setTrimTargetLevels(int numberOfLevelsToTrim) { + this.numberOfTargetLevelsToTrim = numberOfLevelsToTrim; + } + + /** + * Method getCompareTimestamps. + * @return boolean + */ + public boolean getCompareTimestamps() { + return this.compareTimestamps; + } + + /** + * Method setCompareTimestamps. + * @param b boolean + */ + public void setCompareTimestamps(boolean b) { + this.compareTimestamps = b; + } + + /** + * Method getExcludeSCMFiles. + * @return boolean + */ + public boolean getExcludeSCMFiles() { + return this.excludeSCMFiles; + } + + /** + * Method setExcludeSCMFiles. + * @param b boolean + */ + public void setExcludeSCMFiles(boolean b) { + this.excludeSCMFiles = b; + } + + /** + * Method getCompareCRCValues. + * @return boolean + */ + public boolean getCompareCRCValues() { + return this.compareCRCValues; + } + + /** + * Method setCompareCRCValues. + * @param b boolean + */ + public void setCompareCRCValues(boolean b) { + this.compareCRCValues = b; + } + + /** + * Method getExludeRegexp. + * @return Set<String> + */ + public Set getExludeRegexp() { + return this.patterns; + } + + /** + * Method setExcludeRegexp. + * @param excludeRegexp String + */ + public void setExcludeRegexp(String excludeRegexp) { + if (excludeRegexp == null || "".equals(excludeRegexp)) { + return; + } + try { + Pattern.compile(excludeRegexp); + this.patterns.add(excludeRegexp); + } catch (PatternSyntaxException e) { + log("Cannot set up excluderegexp: " + e.getMessage(), Project.MSG_WARN); + } + } + + /** + * gets the name of the output file + * @return output file + */ + public String getOutput() { + return this.output; + } + + /** + * sets the name of the output file + * @param name filename + */ + public void setOutput(String name) { + this.output = name; + } + + /** + * Method setProperty. + * @param name String + */ + public void setProperty(String name) { + this.property = name; + } + + /** + * Method execute. + * @throws BuildException + */ + public void execute() throws BuildException { + validate(); + + // log("Source=" + source, Project.MSG_DEBUG); + // log("Target=" + target, Project.MSG_DEBUG); + // log("Output=" + getOutput(), Project.MSG_DEBUG); + + final Differences diff = calculateDifferences(); + if (!"".equals(this.property) && null == getProject().getProperty(this.property) && diff.hasDifferences()) { + getProject().setNewProperty(this.property, "true"); + } + + try { + writeOutput(diff); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + /** + * writes the output file + * @param d set of Differences + * @throws IOException + */ + protected void writeOutput(Differences d) throws IOException { + final String output = getOutput(); + final Builder builder = BuilderFactory.create(output); + builder.build(output, getTrimOutputLevels(), d); + } + + /** + * calculates the differences + * @return set of Differences + * @throws BuildException in case of an input/output error + */ + protected Differences calculateDifferences() throws BuildException { + Differences diff = null; + + try { + final DifferenceCalculator calculator = new DifferenceCalculator(this.source, this.target); + calculator.setNumberOfSourceLevelsToTrim(getTrimSourceLevels()); + calculator.setNumberOfTargetLevelsToTrim(getTrimTargetLevels()); + calculator.setCompareCRCValues(getCompareCRCValues()); + calculator.setCompareTimestamps(getCompareTimestamps()); + calculator.setExcludeSCMFiles(getExcludeSCMFiles()); + calculator.setExcludeRegex(getExludeRegexp()); + + diff = calculator.getDifferences(); + } catch (IOException ex) { + throw new BuildException(ex); + } + + return diff; + } + + /** + * validates the parameters + * @throws BuildException in case of invalid parameters + */ + protected void validate() throws BuildException { + if ((this.source == null) || (this.source.length() < 1)) { + throw new BuildException("source is required"); + } + + if ((this.target == null) || (this.target.length() < 1)) { + throw new BuildException("target is required"); + } + + final String output = getOutput(); + if ((output == null) || (output.length() < 1)) { + throw new BuildException("output is required"); + } + } +} diff --git a/src/main/java/zipdiff/ant/package.html b/src/main/java/zipdiff/ant/package.html new file mode 100755 index 0000000..c5a743d --- /dev/null +++ b/src/main/java/zipdiff/ant/package.html @@ -0,0 +1,18 @@ + + + + + + + + +Provides Ant tasks for the zipdiff utility. + + + diff --git a/src/main/java/zipdiff/output/AbstractBuilder.java b/src/main/java/zipdiff/output/AbstractBuilder.java new file mode 100644 index 0000000..c352528 --- /dev/null +++ b/src/main/java/zipdiff/output/AbstractBuilder.java @@ -0,0 +1,70 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff.output; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import zipdiff.Differences; + +/** + * abstract base class for Builders. + * + * @author Sean C. Sullivan, Hendrik Brummermann + */ +public abstract class AbstractBuilder implements Builder { + /** + * number of directory levels to trim in the output file + */ + private int numberOfOutputLevelsToTrim; + + /** + * builds the output + * @param filename name of output file + * @param numberOfLevelsToTrim number of directory levels to trim + * @param d differences + * @throws IOException in case of an input/output error + * @see zipdiff.output.Builder#build(String, int, Differences) + */ + public void build(String filename, int numberOfLevelsToTrim, Differences d) throws IOException { + this.numberOfOutputLevelsToTrim = numberOfLevelsToTrim; + + OutputStream os = null; + if ((filename == null) || filename.equals("-")) { + os = System.out; + } else { + final File file = new File(filename); + if (file.isDirectory()) { + System.err.println("File \"" + filename + "\" is a directory, using stdout instead"); + os = System.out; + } else if (file.exists() && !file.canWrite()) { + System.err.println("Cannot write to \"" + filename + "\", using stdout instead"); + os = System.out; + } else { + os = new FileOutputStream(filename); + } + } + build(os, d); + os.flush(); + } + + /** + * Method getTrimOutputLevels. + * @return int + */ + public int getTrimOutputLevels() { + return this.numberOfOutputLevelsToTrim; + } + + /** + * builds the output + * @param out OutputStream to write to + * @param d differences + */ + public abstract void build(OutputStream out, Differences d); +} diff --git a/src/main/zipdiff/output/Builder.java b/src/main/java/zipdiff/output/Builder.java similarity index 55% rename from src/main/zipdiff/output/Builder.java rename to src/main/java/zipdiff/output/Builder.java index ee66d0a..31a8278 100644 --- a/src/main/zipdiff/output/Builder.java +++ b/src/main/java/zipdiff/output/Builder.java @@ -6,24 +6,21 @@ package zipdiff.output; import java.io.IOException; - import zipdiff.Differences; /** - * Builder pattern: - * http://wiki.cs.uiuc.edu/patternStories/BuilderPattern + * Builder pattern: + * http://wiki.cs.uiuc.edu/patternStories/BuilderPattern * * @author Sean C. Sullivan */ public interface Builder { - /** * builds the output - * * @param filename name of output file - * @param numberOfOutputPrefixesToSkip number of directory prefixes to skip + * @param numberOfOutputLevelsToTrim number of directory levels to trim * @param d differences * @throws IOException in case of an input/output error */ - public void build(String filename, int numberOfOutputPrefixesToSkip, Differences d) throws IOException; + void build(String filename, int numberOfOutputLevelsToTrim, Differences d) throws IOException; } diff --git a/src/main/zipdiff/output/BuilderFactory.java b/src/main/java/zipdiff/output/BuilderFactory.java similarity index 99% rename from src/main/zipdiff/output/BuilderFactory.java rename to src/main/java/zipdiff/output/BuilderFactory.java index 0badbeb..f391e58 100644 --- a/src/main/zipdiff/output/BuilderFactory.java +++ b/src/main/java/zipdiff/output/BuilderFactory.java @@ -11,10 +11,8 @@ * @author Hendrik Brummermann, HIS GmbH */ public class BuilderFactory { - /** * creates a builder based on the name of the output file - * * @param filename name of output file * @return Builder */ @@ -23,23 +21,19 @@ public static Builder create(String filename) { if ((filename == null) || filename.equals("-")) { builder = new TextBuilder(); - } else if (filename.endsWith(".html")) { builder = new HtmlBuilder(); - } else if (filename.endsWith(".txt")) { builder = new TextBuilder(); - } else if (filename.endsWith(".xml")) { builder = new XmlBuilder(); - } else if (filename.endsWith(".zip")) { builder = new ZipBuilder(); - } else { System.err.println("Unknown extension, using text output"); builder = new TextBuilder(); } + return builder; } } diff --git a/src/main/java/zipdiff/output/HtmlBuilder.java b/src/main/java/zipdiff/output/HtmlBuilder.java new file mode 100644 index 0000000..d6a4a80 --- /dev/null +++ b/src/main/java/zipdiff/output/HtmlBuilder.java @@ -0,0 +1,112 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff.output; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Date; +import java.util.Set; + +import zipdiff.Differences; + +/** + * Generates html output for a Differences instance + * + * @author Sean C. Sullivan + */ +public class HtmlBuilder extends AbstractBuilder { + /** + * builds the output + * @param out OutputStream to write to + * @param d differences + */ + @Override + public void build(OutputStream out, Differences d) { + String source = d.getSource(); + if (source == null) { + source = "source.zip"; + } + + String target = d.getTarget(); + if (target == null) { + target = "target.zip"; + } + + final PrintWriter pw = new PrintWriter(out); + + pw.println(""); + pw.println(""); + pw.println(""); + pw.println("File differences"); + pw.println(getStyleTag()); + pw.println(""); + + pw.println(""); + + pw.println(String.format("

Source: %s
Target: %s

", source, target)); + + writeDiffSet(pw, "Added", d.getAdded().keySet()); + writeDiffSet(pw, "Removed", d.getRemoved().keySet()); + writeDiffSet(pw, "Changed", d.getChanged().keySet()); + pw.println("
"); + pw.println("

"); + pw.println("Generated at " + new Date()); + pw.println("

"); + pw.println(""); + + pw.println(""); + + pw.flush(); + } + + /** + * writes a set of differences + * @param pw write to write to + * @param name heading + * @param s set + */ + protected void writeDiffSet(PrintWriter pw, String name, Set s) { + pw.println(String.format("

%s (%d entries)

", name, s.size())); + if (s.size() > 0) { + pw.println("
    "); + for (String key: s) { + pw.println(String.format("
  • %s
  • ", key)); + } + pw.println("
"); + } + } + + /** + * generates the HTML style tag. + * @return content of style tag + */ + protected String getStyleTag() { + final StringBuilder sb = new StringBuilder(); + + sb.append("\n"); + + return sb.toString(); + } +} diff --git a/src/main/zipdiff/output/TextBuilder.java b/src/main/java/zipdiff/output/TextBuilder.java similarity index 92% rename from src/main/zipdiff/output/TextBuilder.java rename to src/main/java/zipdiff/output/TextBuilder.java index 11ed5fd..f85c5a3 100644 --- a/src/main/zipdiff/output/TextBuilder.java +++ b/src/main/java/zipdiff/output/TextBuilder.java @@ -16,16 +16,14 @@ * @author Sean C. Sullivan */ public class TextBuilder extends AbstractBuilder { - /** * builds the output - * * @param out OutputStream to write to * @param d differences */ @Override public void build(OutputStream out, Differences d) { - PrintWriter pw = new PrintWriter(out); + final PrintWriter pw = new PrintWriter(out); pw.println(d.toString()); pw.flush(); } diff --git a/src/main/java/zipdiff/output/XmlBuilder.java b/src/main/java/zipdiff/output/XmlBuilder.java new file mode 100644 index 0000000..0c99f22 --- /dev/null +++ b/src/main/java/zipdiff/output/XmlBuilder.java @@ -0,0 +1,62 @@ +/* zipdiff is available under the terms of the + * Apache License, version 2.0 + * + * Link: http://www.apache.org/licenses/ + */ +package zipdiff.output; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Set; + +import zipdiff.Differences; + +/** + * Generates xml output for a Differences instance + * + * @author Sean C. Sullivan + */ +public class XmlBuilder extends AbstractBuilder { + /** + * builds the output + * @param out OutputStream to write to + * @param d differences + */ + @Override + public void build(OutputStream out, Differences d) { + String source = d.getSource(); + if (source == null) { + source = "source.zip"; + } + + String target = d.getTarget(); + if (target == null) { + target = "target.zip"; + } + + final PrintWriter pw = new PrintWriter(out); + + pw.println(""); + pw.print(String.format("", source, target)); + pw.println(""); + writeStatusTags(pw, "added", d.getAdded().keySet()); + writeStatusTags(pw, "removed", d.getRemoved().keySet()); + writeStatusTags(pw, "changed", d.getChanged().keySet()); + pw.println(""); + pw.println(""); + + pw.flush(); + } + + /** + * writes the list of modified files + * @param pw write to + * @param statusTag kind of modification (added, removed, changed) + * @param modified set of modified files + */ + protected void writeStatusTags(PrintWriter pw, String statusTag, Set modified) { + for (String key : modified) { + pw.print(String.format("<%s>%s", statusTag, key, statusTag)); + } + } +} diff --git a/src/main/zipdiff/output/ZipBuilder.java b/src/main/java/zipdiff/output/ZipBuilder.java similarity index 59% rename from src/main/zipdiff/output/ZipBuilder.java rename to src/main/java/zipdiff/output/ZipBuilder.java index facfd37..b20685d 100644 --- a/src/main/zipdiff/output/ZipBuilder.java +++ b/src/main/java/zipdiff/output/ZipBuilder.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -25,23 +24,22 @@ * @author Hendrik Brummermann, HIS GmbH */ public class ZipBuilder extends AbstractBuilder { - private Differences differences; - - private final Set filenames = new TreeSet(); + /** + * Field filenames. + */ + private final Set filenames = new TreeSet(); /** * builds the output - * * @param out OutputStream to write to - * @param d differences + * @param diff differences */ @Override - public void build(OutputStream out, Differences d) { - differences = d; + public void build(OutputStream out, Differences diff) { try { - collectAddedFiles(); - collectModifiedFiles(); - copyEntries(out); + collectAddedFiles(diff); + collectModifiedFiles(diff); + copyEntries(out, diff.getTarget()); } catch (IOException e) { System.err.println("Error while writing zip file: " + e); e.printStackTrace(); @@ -50,48 +48,42 @@ public void build(OutputStream out, Differences d) { /** * collects all the files that have been added in the second zip archive + * @param diff differences */ - private void collectAddedFiles() { - Set entrySet = differences.getAdded().entrySet(); - Iterator itr = entrySet.iterator(); - while (itr.hasNext()) { - Map.Entry mapEntry = (Map.Entry) itr.next(); - if (mapEntry.getKey().toString().indexOf("!") < 0) { - filenames.add(((ZipEntry) mapEntry.getValue()).getName()); + private void collectAddedFiles(Differences diff) { + for (Map.Entry mapEntry : diff.getAdded().entrySet()) { + if (!mapEntry.getKey().contains("!")) { + this.filenames.add((mapEntry.getValue())[0].getName()); } } } /** * collects all the files that have been added modified in the second zip archive + * @param diff differences */ - private void collectModifiedFiles() { - Set entrySet = differences.getChanged().entrySet(); - Iterator itr = entrySet.iterator(); - while (itr.hasNext()) { - Map.Entry mapEntry = (Map.Entry) itr.next(); - if (mapEntry.getKey().toString().indexOf("!") < 0) { - filenames.add(((ZipEntry[]) mapEntry.getValue())[1].getName()); + private void collectModifiedFiles(Differences diff) { + for (Map.Entry mapEntry : diff.getChanged().entrySet()) { + if (!mapEntry.getKey().contains("!")) { + this.filenames.add((mapEntry.getValue())[1].getName()); } } } /** * copies the zip entries (with data) from the second archive file to the output file. - * - * @param out output file + * @param out output stream + * @param target target file name * @throws IOException in case of an input/output error */ - private void copyEntries(OutputStream out) throws IOException { - ZipOutputStream os = new ZipOutputStream(out); - ZipFile zipFile = new ZipFile(differences.getFilename2()); - Iterator itr = filenames.iterator(); + private void copyEntries(OutputStream out, String target) throws IOException { + final ZipOutputStream os = new ZipOutputStream(out); + final ZipFile zipFile = new ZipFile(target); - while (itr.hasNext()) { - String filename = (String) itr.next(); + for (String filename : this.filenames) { ZipEntry zipEntry = zipFile.getEntry(filename); InputStream is = zipFile.getInputStream(zipEntry); - ZipEntry z = new ZipEntry(StringUtil.removeDirectoryPrefix(filename, numberOfOutputPrefixesToSkip)); + ZipEntry z = new ZipEntry(StringUtil.removeDirectoryPrefix(filename, getTrimOutputLevels())); os.putNextEntry(z); copyStream(is, os); os.closeEntry(); @@ -104,13 +96,12 @@ private void copyEntries(OutputStream out) throws IOException { /** * copies data from an input stream to an output stream - * * @param input InputStream * @param output OutputStream * @throws IOException in case of an input/output error */ private void copyStream(InputStream input, OutputStream output) throws IOException { - byte buffer[] = new byte[4096]; + final byte[] buffer = new byte[4096]; int count = input.read(buffer); while (count > -1) { output.write(buffer, 0, count); diff --git a/src/main/zipdiff/output/package.html b/src/main/java/zipdiff/output/package.html similarity index 53% rename from src/main/zipdiff/output/package.html rename to src/main/java/zipdiff/output/package.html index a439889..a11b9f7 100755 --- a/src/main/zipdiff/output/package.html +++ b/src/main/java/zipdiff/output/package.html @@ -1,4 +1,4 @@ - + + - + -Provides the interface and implementations used for formatting the output of the zipdiff Ant tasks. +Provides the interface and implementations used for formatting the output of the zipdiff utility. diff --git a/src/main/java/zipdiff/package.html b/src/main/java/zipdiff/package.html new file mode 100755 index 0000000..5bd7aee --- /dev/null +++ b/src/main/java/zipdiff/package.html @@ -0,0 +1,18 @@ + + + + + + + + +Utilities for comparing the contents of two zip files. + + + diff --git a/src/main/zipdiff/util/StringUtil.java b/src/main/java/zipdiff/util/StringUtil.java similarity index 100% rename from src/main/zipdiff/util/StringUtil.java rename to src/main/java/zipdiff/util/StringUtil.java diff --git a/src/main/java/zipdiff/util/package.html b/src/main/java/zipdiff/util/package.html new file mode 100644 index 0000000..7844c66 --- /dev/null +++ b/src/main/java/zipdiff/util/package.html @@ -0,0 +1,18 @@ + + + + + + + + +Provides string manipulation routines for the zipdiff utility. + + + diff --git a/src/main/resources/META-INF/Ant-JAR-manifest.txt b/src/main/resources/META-INF/Ant-JAR-manifest.txt new file mode 100644 index 0000000..01407da --- /dev/null +++ b/src/main/resources/META-INF/Ant-JAR-manifest.txt @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Created-By: Sean C. Sullivan diff --git a/src/main/resources/META-INF/JAR-manifest.txt b/src/main/resources/META-INF/JAR-manifest.txt new file mode 100644 index 0000000..08e1759 --- /dev/null +++ b/src/main/resources/META-INF/JAR-manifest.txt @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Created-By: Sean C. Sullivan +Class-Path: commons-cli.jar +Main-Class: zipdiff.Main diff --git a/src/main/resources/zipdiff/ant/antlib.xml b/src/main/resources/zipdiff/ant/antlib.xml new file mode 100644 index 0000000..d3b6e1e --- /dev/null +++ b/src/main/resources/zipdiff/ant/antlib.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/main/zipdiff/DifferenceCalculator.java b/src/main/zipdiff/DifferenceCalculator.java deleted file mode 100644 index 72f6b4b..0000000 --- a/src/main/zipdiff/DifferenceCalculator.java +++ /dev/null @@ -1,468 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; - -import zipdiff.util.StringUtil; - -/** - * Checks and compiles differences between two zip files. - * It also has the ability to exclude entries from the comparison - * based on a regular expression. - * - * @author Sean C. Sullivan, Hendrik Brummermann - */ -public class DifferenceCalculator { - - private final Logger logger = Logger.getLogger(getClass().getName()); - - private final ZipFile file1; - - private final ZipFile file2; - - private int numberOfPrefixesToSkip1 = 0; - - private int numberOfPrefixesToSkip2 = 0; - - private boolean ignoreTimestamps = false; - - private boolean ignoreCVSFiles = false; - - private boolean compareCRCValues = true; - - private Pattern filesToIgnorePattern; - - private boolean bVerbose = false; - - protected void debug(Object msg) { - if (isVerboseEnabled()) { - System.out.println("[" + DifferenceCalculator.class.getName() + "] " + String.valueOf(msg)); - } - } - - /** - * Set the verboseness of the debug output. - * @param b true to make verbose - */ - public void setVerbose(boolean b) { - bVerbose = b; - } - - protected boolean isVerboseEnabled() { - return bVerbose; - } - - /** - * Constructor taking 2 filenames to compare - * @throws java.io.IOException - */ - public DifferenceCalculator(String filename1, String filename2) throws java.io.IOException { - this(new File(filename1), new File(filename2)); - } - - /** - * Constructor taking 2 Files to compare - * @throws java.io.IOException - */ - public DifferenceCalculator(File f1, File f2) throws java.io.IOException { - this(new ZipFile(f1), new ZipFile(f2)); - } - - /** - * Constructor taking 2 ZipFiles to compare - */ - public DifferenceCalculator(ZipFile zf1, ZipFile zf2) { - file1 = zf1; - file2 = zf2; - } - - /** - * - * @param Set A set of regular expressions that when matched against a ZipEntry - * then that ZipEntry will be ignored from the comparison. - * @see java.util.regex - */ - public void setFilenameRegexToIgnore(Set patterns) { - if (patterns == null) { - filesToIgnorePattern = null; - } else if (patterns.isEmpty()) { - filesToIgnorePattern = null; - } else { - String regex = ""; - - Iterator iter = patterns.iterator(); - while (iter.hasNext()) { - String pattern = (String) iter.next(); - if (regex.length() > 0) { - regex += "|"; - } - regex += "(" + pattern + ")"; - } - filesToIgnorePattern = Pattern.compile(regex); - logger.log(Level.FINE, "Regular expression is : " + regex); - } - } - - /** - * returns true if fileToIgnorePattern matches the filename given. - * @param filepath - * @param filename The name of the file to check to see if it should be ignored. - * @return true if the file should be ignored. - */ - protected boolean ignoreThisFile(String filepath, String entryName) { - if (entryName == null) { - return false; - } else if (isCVSFile(filepath, entryName) && (ignoreCVSFiles())) { - return true; - } else if (filesToIgnorePattern == null) { - return false; - } else { - Matcher m = filesToIgnorePattern.matcher(entryName); - boolean match = m.matches(); - if (match) { - logger.log(Level.FINEST, "Found a match against : " + entryName + " so excluding"); - } - return match; - } - } - - protected boolean isCVSFile(String filepath, String entryName) { - if (entryName == null) { - return false; - } else if ((filepath.indexOf("CVS/") != -1) || (entryName.indexOf("CVS/") != -1)) { - return true; - } else { - return false; - } - } - - /** - * Ensure that the comparison checks against the CRCs of the entries. - * @param b true ensures that CRCs will be checked - */ - public void setCompareCRCValues(boolean b) { - compareCRCValues = b; - } - - /** - * @return true if this instance will check the CRCs of each ZipEntry - */ - public boolean getCompareCRCValues() { - return compareCRCValues; - } - - /** - * sets the number of directory prefixes to skip in the first file - * - * @param numberOfPrefixesToSkip1 number of directory prefixes to skip - */ - public void setNumberOfPrefixesToSkip1(int numberOfPrefixesToSkip1) { - this.numberOfPrefixesToSkip1 = numberOfPrefixesToSkip1; - } - - /** - * @return number of directory prefixes to skip - */ - public int getNumberOfPrefixesToSkip1() { - return numberOfPrefixesToSkip1; - } - - /** - * sets the number of directory prefixes to skip in the first file - * - * @param numberOfPrefixesToSkip2 number of directory prefixes to skip - */ - public void setNumberOfPrefixesToSkip2(int numberOfPrefixesToSkip2) { - this.numberOfPrefixesToSkip2 = numberOfPrefixesToSkip2; - } - - /** - * @return number of directory prefixes to skip - */ - public int getNumberOfPrefixesToSkip2() { - return numberOfPrefixesToSkip2; - } - - - /** - * Opens the ZipFile and builds up a map of all the entries. The key is the name of - * the entry and the value is the ZipEntry itself. - * @param zf The ZipFile for which to build up the map of ZipEntries - * @return The map containing all the ZipEntries. The key being the name of the ZipEntry. - * @throws java.io.IOException - * @Deprecated - */ - protected Map buildZipEntryMap(ZipFile zf) throws java.io.IOException { - return buildZipEntryMap(zf, 0); - } - - /** - * Opens the ZipFile and builds up a map of all the entries. The key is the name of - * the entry and the value is the ZipEntry itself. - * @param zf The ZipFile for which to build up the map of ZipEntries - * @param number of directory prefixes to skip - * @return The map containing all the ZipEntries. The key being the name of the ZipEntry. - * @throws java.io.IOException - */ - protected Map buildZipEntryMap(ZipFile zf, int p) throws java.io.IOException { - Map zipEntryMap = new HashMap(); - try { - Enumeration entries = zf.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = (ZipEntry) entries.nextElement(); - InputStream is = null; - try { - is = zf.getInputStream(entry); - processZipEntry("", entry, is, zipEntryMap, p); - } finally { - if (is != null) { - is.close(); - } - } - } - } finally { - zf.close(); - } - - return zipEntryMap; - } - - /** - * Will place ZipEntries for a given ZipEntry into the given Map. More ZipEntries will result - * if zipEntry is itself a ZipFile. All embedded ZipFiles will be processed with their names - * prefixed onto the names of their ZipEntries. - * @param prefix The prefix of the ZipEntry that should be added to the key. Typically used - * when processing embedded ZipFiles. The name of the embedded ZipFile would be the prefix of - * all the embedded ZipEntries. - * @param zipEntry The ZipEntry to place into the Map. If it is a ZipFile then all its ZipEntries - * will also be placed in the Map. - * @param is The InputStream of the corresponding ZipEntry. - * @param zipEntryMap The Map in which to place all the ZipEntries into. The key will - * be the name of the ZipEntry. - * @throws IOException - * @Deprecated - */ - protected void processZipEntry(String prefix, ZipEntry zipEntry, InputStream is, Map zipEntryMap) throws IOException { - processZipEntry(prefix, zipEntry, is, zipEntryMap, 0); - } - - - /** - * Will place ZipEntries for a given ZipEntry into the given Map. More ZipEntries will result - * if zipEntry is itself a ZipFile. All embedded ZipFiles will be processed with their names - * prefixed onto the names of their ZipEntries. - * @param prefix The prefix of the ZipEntry that should be added to the key. Typically used - * when processing embedded ZipFiles. The name of the embedded ZipFile would be the prefix of - * all the embedded ZipEntries. - * @param zipEntry The ZipEntry to place into the Map. If it is a ZipFile then all its ZipEntries - * will also be placed in the Map. - * @param is The InputStream of the corresponding ZipEntry. - * @param zipEntryMap The Map in which to place all the ZipEntries into. The key will - * be the name of the ZipEntry. - * @param p number of directory prefixes to skip - * @throws IOException - */ - protected void processZipEntry(String prefix, ZipEntry zipEntry, InputStream is, Map zipEntryMap, int p) throws IOException { - if (ignoreThisFile(prefix, zipEntry.getName())) { - logger.log(Level.FINE, "ignoring file: " + zipEntry.getName()); - } else { - String name = StringUtil.removeDirectoryPrefix(prefix + zipEntry.getName(), p); - if ((name == null) || name.equals("")) { - return; - } - - logger.log(Level.FINEST, "processing ZipEntry: " + name); - zipEntryMap.put(name, zipEntry); - - if (!zipEntry.isDirectory() && isZipFile(name)) { - processEmbeddedZipFile(name + "!", is, zipEntryMap); - } - } - } - - - - protected void processEmbeddedZipFile(String prefix, InputStream is, Map m) throws java.io.IOException { - ZipInputStream zis = new ZipInputStream(is); - - ZipEntry entry = zis.getNextEntry(); - - while (entry != null) { - processZipEntry(prefix, entry, zis, m); - zis.closeEntry(); - entry = zis.getNextEntry(); - } - - } - - /** - * Returns true if the filename has a valid zip extension. - * i.e. jar, war, ear, zip etc. - * @param filename The name of the file to check. - * @return true if it has a valid extension. - */ - public static boolean isZipFile(String filename) { - boolean result; - - if (filename == null) { - result = false; - } else { - String lowercaseName = filename.toLowerCase(); - if (lowercaseName.endsWith(".zip")) { - result = true; - } else if (lowercaseName.endsWith(".ear")) { - result = true; - } else if (lowercaseName.endsWith(".war")) { - result = true; - } else if (lowercaseName.endsWith(".rar")) { - result = true; - } else if (lowercaseName.endsWith(".jar")) { - result = true; - } else { - result = false; - } - } - - return result; - } - - /** - * Calculates all the differences between two zip files. - * It builds up the 2 maps of ZipEntries for the two files - * and then compares them. - * @param zf1 The first ZipFile to compare - * @param zf2 The second ZipFile to compare - * @return All the differences between the two files. - * @throws java.io.IOException - * @Deprecated - */ - protected Differences calculateDifferences(ZipFile zf1, ZipFile zf2) throws java.io.IOException { - return calculateDifferences(zf1, zf2, 0, 0); - } - - /** - * Calculates all the differences between two zip files. - * It builds up the 2 maps of ZipEntries for the two files - * and then compares them. - * @param zf1 The first ZipFile to compare - * @param zf2 The second ZipFile to compare - * @param p1 number of directory prefixes to skip in the 1st file - * @param p2 number of directory prefixes to skip in the 2nd file - * @return All the differences between the two files. - * @throws java.io.IOException - */ - protected Differences calculateDifferences(ZipFile zf1, ZipFile zf2, int p1, int p2) throws java.io.IOException { - Map map1 = buildZipEntryMap(zf1, p1); - Map map2 = buildZipEntryMap(zf2, p2); - - return calculateDifferences(map1, map2); - } - - /** - * Given two Maps of ZipEntries it will generate a Differences of all the - * differences found between the two maps. - * @return All the differences found between the two maps - */ - protected Differences calculateDifferences(Map m1, Map m2) { - Differences d = new Differences(); - - Set names1 = m1.keySet(); - Set names2 = m2.keySet(); - - Set allNames = new HashSet(); - allNames.addAll(names1); - allNames.addAll(names2); - - Iterator iterAllNames = allNames.iterator(); - while (iterAllNames.hasNext()) { - String name = (String) iterAllNames.next(); - if (ignoreThisFile("", name)) { - // do nothing - } else if (names1.contains(name) && (!names2.contains(name))) { - d.fileRemoved(name, (ZipEntry) m1.get(name)); - } else if (names2.contains(name) && (!names1.contains(name))) { - d.fileAdded(name, (ZipEntry) m2.get(name)); - } else if (names1.contains(name) && (names2.contains(name))) { - ZipEntry entry1 = (ZipEntry) m1.get(name); - ZipEntry entry2 = (ZipEntry) m2.get(name); - if (!entriesMatch(entry1, entry2)) { - d.fileChanged(name, entry1, entry2); - } - } else { - throw new IllegalStateException("unexpected state"); - } - } - - return d; - } - - /** - * returns true if the two entries are equivalent in type, name, size, compressed size - * and time or CRC. - * @param entry1 The first ZipEntry to compare - * @param entry2 The second ZipEntry to compare - * @return true if the entries are equivalent. - */ - protected boolean entriesMatch(ZipEntry entry1, ZipEntry entry2) { - boolean result; - - result = (entry1.isDirectory() == entry2.isDirectory()) && (entry1.getSize() == entry2.getSize()) && (entry1.getCompressedSize() == entry2.getCompressedSize()); - - if (!isIgnoringTimestamps()) { - result = result && (entry1.getTime() == entry2.getTime()); - } - - if (getCompareCRCValues()) { - result = result && (entry1.getCrc() == entry2.getCrc()); - } - return result; - } - - public void setIgnoreTimestamps(boolean b) { - ignoreTimestamps = b; - } - - public boolean isIgnoringTimestamps() { - return ignoreTimestamps; - } - - public boolean ignoreCVSFiles() { - return ignoreCVSFiles; - } - - public void setIgnoreCVSFiles(boolean b) { - ignoreCVSFiles = b; - } - - /** - * - * @return all the differences found between the two zip files. - * @throws java.io.IOException - */ - public Differences getDifferences() throws java.io.IOException { - Differences d = calculateDifferences(file1, file2, numberOfPrefixesToSkip1, numberOfPrefixesToSkip2); - d.setFilename1(file1.getName()); - d.setFilename2(file2.getName()); - - return d; - } -} diff --git a/src/main/zipdiff/Differences.java b/src/main/zipdiff/Differences.java deleted file mode 100644 index cda88ee..0000000 --- a/src/main/zipdiff/Differences.java +++ /dev/null @@ -1,143 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff; - -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.zip.ZipEntry; - -/** - * Used to keep track of difference between 2 zip files. - * - * @author Sean C. Sullivan - */ -public class Differences { - private final Map added = new TreeMap(); - - private final Map removed = new TreeMap(); - - private final Map changed = new TreeMap(); - - private final Map ignored = new TreeMap(); - - private String filename1; - - private String filename2; - - public Differences() { - // todo - } - - public void setFilename1(String filename) { - filename1 = filename; - } - - public void setFilename2(String filename) { - filename2 = filename; - } - - public String getFilename1() { - return filename1; - } - - public String getFilename2() { - return filename2; - } - - public void fileAdded(String fqn, ZipEntry ze) { - added.put(fqn, ze); - } - - public void fileRemoved(String fqn, ZipEntry ze) { - removed.put(fqn, ze); - } - - public void fileIgnored(String fqn, ZipEntry ze) { - ignored.put(fqn, ze); - } - - public void fileChanged(String fqn, ZipEntry z1, ZipEntry z2) { - ZipEntry[] entries = new ZipEntry[2]; - entries[0] = z1; - entries[1] = z2; - changed.put(fqn, entries); - } - - public Map getAdded() { - return added; - } - - public Map getRemoved() { - return removed; - } - - public Map getChanged() { - return changed; - } - - public Map getIgnored() { - return ignored; - } - - public boolean hasDifferences() { - return ((getChanged().size() > 0) || (getAdded().size() > 0) || (getRemoved().size() > 0)); - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - - if (added.size() == 1) { - sb.append("1 file was"); - } else { - sb.append(added.size() + " files were"); - } - sb.append(" added to " + this.getFilename2() + "\n"); - - Iterator iter = added.keySet().iterator(); - while (iter.hasNext()) { - String name = (String) iter.next(); - sb.append("\t[added] " + name + "\n"); - } - - if (removed.size() == 1) { - sb.append("1 file was"); - } else { - sb.append(removed.size() + " files were"); - } - sb.append(" removed from " + this.getFilename2() + "\n"); - - iter = removed.keySet().iterator(); - while (iter.hasNext()) { - String name = (String) iter.next(); - sb.append("\t[removed] " + name + "\n"); - } - - if (changed.size() == 1) { - sb.append("1 file changed\n"); - } else { - sb.append(changed.size() + " files changed\n"); - } - - Set keys = getChanged().keySet(); - iter = keys.iterator(); - while (iter.hasNext()) { - String name = (String) iter.next(); - ZipEntry[] entries = (ZipEntry[]) getChanged().get(name); - sb.append("\t[changed] " + name + " "); - sb.append(" ( size " + entries[0].getSize()); - sb.append(" : " + entries[1].getSize()); - sb.append(" )\n"); - } - int differenceCount = added.size() + changed.size() + removed.size(); - - sb.append("Total differences: " + differenceCount); - return sb.toString(); - } - -} diff --git a/src/main/zipdiff/Main.java b/src/main/zipdiff/Main.java deleted file mode 100644 index b93f36e..0000000 --- a/src/main/zipdiff/Main.java +++ /dev/null @@ -1,249 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.GnuParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -import zipdiff.output.Builder; -import zipdiff.output.BuilderFactory; - -/** - * Provides a command line interface to zipdiff - * - * @author Sean C. Sullivan, J.Stewart, Hendrik Brummermann - */ -public class Main { - private static final int EXITCODE_ERROR = 2; - - private static final int EXITCODE_DIFF = 1; - - private static final String OPTION_COMPARE_CRC_VALUES = "comparecrcvalues"; - - private static final String OPTION_COMPARE_TIMESTAMPS = "comparetimestamps"; - - private static final String OPTION_IGNORE_CVS_FILES = "ignorecvsfiles"; - - private static final String OPTION_OUTPUT_FILE = "outputfile"; - - private static final String OPTION_FILE1 = "file1"; - - private static final String OPTION_FILE2 = "file2"; - - private static final String OPTION_SKIP_OUTPUT_PREFIXES = "skipoutputprefixes"; - - private static final String OPTION_SKIP_PREFIX1 = "skipprefixes1"; - - private static final String OPTION_SKIP_PREFIX2 = "skipprefixes2"; - - private static final String OPTION_REGEX = "regex"; - - private static final String OPTION_EXIT_WITH_ERROR_ON_DIFF = "exitwitherrorondifference"; - - private static final String OPTION_VERBOSE = "verbose"; - - private static final Options options; - - // static initializer - static { - options = new Options(); - - Option compareTS = new Option(OPTION_COMPARE_TIMESTAMPS, OPTION_COMPARE_TIMESTAMPS, false, "Compare timestamps"); - compareTS.setRequired(false); - - Option compareCRC = new Option(OPTION_COMPARE_CRC_VALUES, OPTION_COMPARE_CRC_VALUES, false, "Compare CRC values"); - compareCRC.setRequired(false); - - Option file1 = new Option(OPTION_FILE1, OPTION_FILE1, true, " first file to compare"); - file1.setRequired(true); - - Option file2 = new Option(OPTION_FILE2, OPTION_FILE2, true, " second file to compare"); - file2.setRequired(true); - - Option numberOfOutputPrefixesToSkip = new Option(OPTION_SKIP_OUTPUT_PREFIXES, OPTION_SKIP_OUTPUT_PREFIXES, true, " number of directory prefix to skip in the output file (if supported by outputter"); - numberOfOutputPrefixesToSkip.setRequired(false); - - - Option numberOfPrefixesToSkip1 = new Option(OPTION_SKIP_PREFIX1, OPTION_SKIP_PREFIX1, true, " number of directory prefix to skip for the first file"); - numberOfPrefixesToSkip1.setRequired(false); - - Option numberOfPrefixesToSkip2 = new Option(OPTION_SKIP_PREFIX2, OPTION_SKIP_PREFIX2, true, " number of directory prefix to skip for the second file"); - numberOfPrefixesToSkip2.setRequired(false); - - Option outputFileOption = new Option(OPTION_OUTPUT_FILE, OPTION_OUTPUT_FILE, true, "output filename"); - outputFileOption.setRequired(false); - - Option regex = new Option(OPTION_REGEX, OPTION_REGEX, true, "regular expression to match files to exclude e.g. (?i)meta-inf.*"); - regex.setRequired(false); - - Option ignoreCVSFilesOption = new Option(OPTION_IGNORE_CVS_FILES, OPTION_IGNORE_CVS_FILES, false, "ignore CVS files"); - ignoreCVSFilesOption.setRequired(false); - - Option exitWithError = new Option(OPTION_EXIT_WITH_ERROR_ON_DIFF, OPTION_EXIT_WITH_ERROR_ON_DIFF, false, "if a difference is found then exit with error " + EXITCODE_DIFF); - - Option verboseOption = new Option(OPTION_VERBOSE, OPTION_VERBOSE, false, "verbose mode"); - - options.addOption(compareTS); - options.addOption(compareCRC); - options.addOption(file1); - options.addOption(file2); - options.addOption(numberOfOutputPrefixesToSkip); - options.addOption(numberOfPrefixesToSkip1); - options.addOption(numberOfPrefixesToSkip2); - options.addOption(regex); - options.addOption(ignoreCVSFilesOption); - options.addOption(exitWithError); - options.addOption(verboseOption); - options.addOption(outputFileOption); - } - - private static void checkFile(java.io.File f) { - String filename = f.toString(); - - if (!f.exists()) { - System.err.println("'" + filename + "' does not exist"); - System.exit(EXITCODE_ERROR); - } - - if (!f.canRead()) { - System.err.println("'" + filename + "' is not readable"); - System.exit(EXITCODE_ERROR); - } - - if (f.isDirectory()) { - System.err.println("'" + filename + "' is a directory"); - System.exit(EXITCODE_ERROR); - } - - } - - private static void writeOutputFile(String filename, int numberOfOutputPrefixesToSkip, Differences d) throws java.io.IOException { - Builder builder = BuilderFactory.create(filename); - builder.build(filename, numberOfOutputPrefixesToSkip, d); - } - - /** - * - * The command line interface to zipdiff utility - * - * @param args The command line parameters - * - */ - public static void main(String[] args) { - CommandLineParser parser = new GnuParser(); - - try { - CommandLine line = parser.parse(options, args); - - String filename1 = null; - String filename2 = null; - - filename1 = line.getOptionValue(OPTION_FILE1); - filename2 = line.getOptionValue(OPTION_FILE2); - - File f1 = new File(filename1); - File f2 = new File(filename2); - - checkFile(f1); - checkFile(f2); - - System.out.println("File 1 = " + f1); - System.out.println("File 2 = " + f2); - - DifferenceCalculator calc = new DifferenceCalculator(f1, f2); - - int numberOfPrefixesToSkip1 = 0; - if (line.getOptionValue(OPTION_SKIP_PREFIX1) != null) { - numberOfPrefixesToSkip1 = Integer.parseInt(line.getOptionValue(OPTION_SKIP_PREFIX1)); - } - int numberOfPrefixesToSkip2 = 0; - if (line.getOptionValue(OPTION_SKIP_PREFIX2) != null) { - numberOfPrefixesToSkip2 = Integer.parseInt(line.getOptionValue(OPTION_SKIP_PREFIX2)); - } - int numberOfOutputPrefixesToSkip = 0; - if (line.getOptionValue(OPTION_SKIP_OUTPUT_PREFIXES) != null) { - numberOfOutputPrefixesToSkip = Integer.parseInt(line.getOptionValue(OPTION_SKIP_OUTPUT_PREFIXES)); - } - - calc.setNumberOfPrefixesToSkip1(numberOfPrefixesToSkip1); - calc.setNumberOfPrefixesToSkip2(numberOfPrefixesToSkip2); - - String regularExpression = null; - - // todo - calc.setFilenamesToIgnore(); - - if (line.hasOption(OPTION_COMPARE_CRC_VALUES)) { - calc.setCompareCRCValues(true); - } else { - calc.setCompareCRCValues(false); - } - - if (line.hasOption(OPTION_IGNORE_CVS_FILES)) { - calc.setIgnoreCVSFiles(true); - } else { - calc.setIgnoreCVSFiles(false); - } - - if (line.hasOption(OPTION_COMPARE_TIMESTAMPS)) { - calc.setIgnoreTimestamps(false); - } else { - calc.setIgnoreTimestamps(true); - } - - if (line.hasOption(OPTION_REGEX)) { - regularExpression = line.getOptionValue(OPTION_REGEX); - Set regexSet = new HashSet(); - regexSet.add(regularExpression); - - calc.setFilenameRegexToIgnore(regexSet); - } - - boolean exitWithErrorOnDiff = false; - if (line.hasOption(OPTION_EXIT_WITH_ERROR_ON_DIFF)) { - exitWithErrorOnDiff = true; - } - - Differences d = calc.getDifferences(); - - if (line.hasOption(OPTION_OUTPUT_FILE)) { - String outputFilename = line.getOptionValue(OPTION_OUTPUT_FILE); - writeOutputFile(outputFilename, numberOfOutputPrefixesToSkip, d); - } - - - if (d.hasDifferences()) { - if (line.hasOption(OPTION_VERBOSE)) { - System.out.println(d); - System.out.println(d.getFilename1() + " and " + d.getFilename2() + " are different."); - } - if (exitWithErrorOnDiff) { - System.exit(EXITCODE_DIFF); - } - } else { - System.out.println("No differences found."); - } - } catch (ParseException pex) { - System.err.println(pex.getMessage()); - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp("zipdiff.Main [options] ", options); - System.exit(EXITCODE_ERROR); - } catch (Exception ex) { - ex.printStackTrace(); - System.exit(EXITCODE_ERROR); - } - - } - -} diff --git a/src/main/zipdiff/ant/ZipDiffTask.java b/src/main/zipdiff/ant/ZipDiffTask.java deleted file mode 100644 index 20c1572..0000000 --- a/src/main/zipdiff/ant/ZipDiffTask.java +++ /dev/null @@ -1,199 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff.ant; - -import java.io.IOException; - -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Task; - -import zipdiff.DifferenceCalculator; -import zipdiff.Differences; -import zipdiff.output.Builder; -import zipdiff.output.BuilderFactory; - -/** - * Ant task for running zipdiff from a build.xml file - * - * - * @author Sean C. Sullivan - */ -public class ZipDiffTask extends Task { - private String filename1; - - private String filename2; - - private int numberOfOutputPrefixesToSkip; - - private int skipPrefixes1 = 0; - - private int SkipPrefixes2 = 0; - - private String destfile; - - private boolean ignoreTimestamps = false; - - private boolean ignoreCVSFiles = false; - - private boolean compareCRCValues = true; - - public void setFilename1(String name) { - filename1 = name; - } - - public void setFilename2(String name) { - filename2 = name; - } - - - - public int getNumberOfOutputPrefixesToSkip() { - return numberOfOutputPrefixesToSkip; - } - - public void setNumberOfOutputPrefixesToSkip(int numberOfOutputPrefixesToSkip) { - this.numberOfOutputPrefixesToSkip = numberOfOutputPrefixesToSkip; - } - - public int getSkipPrefixes1() { - return skipPrefixes1; - } - - public void setSkipPrefixes1(int numberOfPrefixesToSkip1) { - this.skipPrefixes1 = numberOfPrefixesToSkip1; - } - - public int getSkipPrefixes2() { - return SkipPrefixes2; - } - - public void setSkipPrefixes2(int numberOfPrefixesToSkip2) { - this.SkipPrefixes2 = numberOfPrefixesToSkip2; - } - - public void setIgnoreTimestamps(boolean b) { - ignoreTimestamps = b; - } - - public boolean getIgnoreTimestamps() { - return ignoreTimestamps; - } - - public void setIgnoreCVSFiles(boolean b) { - ignoreCVSFiles = b; - } - - public boolean getIgnoreCVSFiles() { - return ignoreCVSFiles; - } - - public void setCompareCRCValues(boolean b) { - compareCRCValues = b; - } - - public boolean getCompareCRCValues() { - return compareCRCValues; - } - - @Override - public void execute() throws BuildException { - validate(); - - // this.log("Filename1=" + filename1, Project.MSG_DEBUG); - // this.log("Filename2=" + filename2, Project.MSG_DEBUG); - // this.log("destfile=" + getDestFile(), Project.MSG_DEBUG); - - Differences d = calculateDifferences(); - - try { - writeDestFile(d); - } catch (java.io.IOException ex) { - throw new BuildException(ex); - } - - } - - /** - * writes the output file - * - * @param d set of Differences - * @throws IOException - */ - protected void writeDestFile(Differences d) throws IOException { - String destfilename = getDestFile(); - Builder builder = BuilderFactory.create(destfilename); - builder.build(destfilename, numberOfOutputPrefixesToSkip, d); - } - - /** - * gets the name of the target file - * - * @return target file - */ - public String getDestFile() { - return destfile; - } - - /** - * sets the name of the target file - * - * @param name filename - */ - public void setDestFile(String name) { - destfile = name; - } - - /** - * calculates the differences - * - * @return set of Differences - * @throws BuildException in case of an input/output error - */ - protected Differences calculateDifferences() throws BuildException { - DifferenceCalculator calculator; - - Differences d = null; - - try { - calculator = new DifferenceCalculator(filename1, filename2); - calculator.setNumberOfPrefixesToSkip1(skipPrefixes1); - calculator.setNumberOfPrefixesToSkip2(SkipPrefixes2); - calculator.setCompareCRCValues(getCompareCRCValues()); - calculator.setIgnoreTimestamps(getIgnoreTimestamps()); - calculator.setIgnoreCVSFiles(getIgnoreCVSFiles()); - - // todo : calculator.setFilenamesToIgnore(patterns); - - d = calculator.getDifferences(); - } catch (IOException ex) { - throw new BuildException(ex); - } - - return d; - } - - /** - * validates the parameters - * - * @throws BuildException in case of invalid parameters - */ - protected void validate() throws BuildException { - if ((filename1 == null) || (filename1.length() < 1)) { - throw new BuildException("filename1 is required"); - } - - if ((filename2 == null) || (filename2.length() < 1)) { - throw new BuildException("filename2 is required"); - } - - String destinationfile = getDestFile(); - - if ((destinationfile == null) || (destinationfile.length() < 1)) { - throw new BuildException("destfile is required"); - } - } - -} diff --git a/src/main/zipdiff/ant/package.html b/src/main/zipdiff/ant/package.html deleted file mode 100755 index 4f9824d..0000000 --- a/src/main/zipdiff/ant/package.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - -Provides ant tasks for the zipdiff utility. - - - diff --git a/src/main/zipdiff/output/AbstractBuilder.java b/src/main/zipdiff/output/AbstractBuilder.java deleted file mode 100644 index c450b05..0000000 --- a/src/main/zipdiff/output/AbstractBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff.output; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import zipdiff.Differences; - -/** - * abstract base class for Builders. - * - * @author Sean C. Sullivan, Hendrik Brummermann - */ -public abstract class AbstractBuilder implements Builder { - - /** number of directory prefixes to skip in the output file */ - protected int numberOfOutputPrefixesToSkip; - - /** - * builds the output - * - * @param filename name of output file - * @param numberOfPrefixesToSkip number of directory prefixes to skip - * @param d differences - * @throws IOException in case of an input/output error - */ - public void build(String filename, int numberOfPrefixesToSkip, Differences d) throws IOException { - this.numberOfOutputPrefixesToSkip = numberOfPrefixesToSkip; - OutputStream os = null; - if ((filename == null) || filename.equals("-")) { - os = System.out; - } else { - os = new FileOutputStream(filename); - } - build(os, d); - os.flush(); - } - - /** - * builds the output - * - * @param out OutputStream to write to - * @param d differences - */ - public abstract void build(OutputStream out, Differences d); -} diff --git a/src/main/zipdiff/output/HtmlBuilder.java b/src/main/zipdiff/output/HtmlBuilder.java deleted file mode 100644 index 506ae73..0000000 --- a/src/main/zipdiff/output/HtmlBuilder.java +++ /dev/null @@ -1,169 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff.output; - -import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.Iterator; -import java.util.Set; - -import zipdiff.Differences; - -/** - * Generates html output for a Differences instance - * - * @author Sean C. Sullivan - */ -public class HtmlBuilder extends AbstractBuilder { - - /** - * builds the output - * - * @param out OutputStream to write to - * @param d differences - */ - @Override - public void build(OutputStream out, Differences d) { - PrintWriter pw = new PrintWriter(out); - - pw.println(""); - pw.println(""); - pw.println(""); - pw.println("File differences"); - pw.println(""); - - pw.println(""); - - pw.println(getStyleTag()); - pw.print("

First file: "); - String filename1 = d.getFilename1(); - - if (filename1 == null) { - filename1 = "filename1.zip"; - } - pw.print(filename1); - pw.println("
"); - - pw.print("Second file: "); - - String filename2 = d.getFilename2(); - - if (filename2 == null) { - filename2 = "filename2.zip"; - } - pw.print(filename2); - pw.println("

"); - - writeAdded(pw, d.getAdded().keySet()); - writeRemoved(pw, d.getRemoved().keySet()); - writeChanged(pw, d.getChanged().keySet()); - pw.println("
"); - pw.println("

"); - pw.println("Generated at " + new java.util.Date()); - pw.println("

"); - pw.println(""); - - pw.println(""); - - pw.flush(); - - } - - /** - * writes the list of added files - * - * @param pw write to write to - * @param added set of added files - */ - protected void writeAdded(PrintWriter pw, Set added) { - writeDiffSet(pw, "Added", added); - } - - /** - * writes the list of removed files - * - * @param pw write to write to - * @param removed set of removed files - */ - protected void writeRemoved(PrintWriter pw, Set removed) { - writeDiffSet(pw, "Removed", removed); - } - - /** - * writes the list of modified files - * - * @param pw write to write to - * @param changed set of modified files - */ - protected void writeChanged(PrintWriter pw, Set changed) { - writeDiffSet(pw, "Changed", changed); - } - - /** - * writes a set of differences - * - * @param pw write to write to - * @param name heading - * @param s set - */ - protected void writeDiffSet(PrintWriter pw, String name, Set s) { - pw.println(""); - pw.println(""); - pw.println(""); - pw.println(""); - pw.println(""); - pw.println(""); - pw.println(""); - pw.println(""); - pw.println("
" + name + " (" + s.size() + " entries)
"); - pw.println(""); - if (s.size() > 0) { - pw.println("
    "); - Iterator iter = s.iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - pw.print("
  • "); - pw.print(key); - pw.println("
  • "); - } - pw.println("
"); - } - pw.println("
"); - - } - - /** - * generates the style-html-tag. - * - * @return content of style-tag - */ - protected String getStyleTag() { - StringBuffer sb = new StringBuffer(); - - sb.append("\n"); - - return sb.toString(); - } - -} diff --git a/src/main/zipdiff/output/XmlBuilder.java b/src/main/zipdiff/output/XmlBuilder.java deleted file mode 100644 index 3cbe9c9..0000000 --- a/src/main/zipdiff/output/XmlBuilder.java +++ /dev/null @@ -1,112 +0,0 @@ -/* zipdiff is available under the terms of the - * Apache License, version 2.0 - * - * Link: http://www.apache.org/licenses/ - */ -package zipdiff.output; - -import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.Iterator; -import java.util.Set; - -import zipdiff.Differences; - -/** - * - * Generates xml output for a Differences instance - * - * @author Sean C. Sullivan - * - */ -public class XmlBuilder extends AbstractBuilder { - - /** - * builds the output - * - * @param out OutputStream to write to - * @param d differences - */ - @Override - public void build(OutputStream out, Differences d) { - PrintWriter pw = new PrintWriter(out); - - pw.println(""); - pw.print(""); - - pw.println(""); - writeAdded(pw, d.getAdded().keySet()); - writeRemoved(pw, d.getRemoved().keySet()); - writeChanged(pw, d.getChanged().keySet()); - pw.println(""); - pw.println(""); - - pw.flush(); - } - - /** - * writes the list of added files - * - * @param pw write to write to - * @param added set of added files - */ - protected void writeAdded(PrintWriter pw, Set added) { - Iterator iter = added.iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - pw.print(""); - pw.print(key); - pw.println(""); - } - - } - - /** - * writes the list of removed files - * - * @param pw write to write to - * @param removed set of removed files - */ - protected void writeRemoved(PrintWriter pw, Set removed) { - Iterator iter = removed.iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - pw.print(""); - pw.print(key); - pw.println(""); - } - } - - /** - * writes the list of modified files - * - * @param pw write to write to - * @param changed set of modified files - */ - protected void writeChanged(PrintWriter pw, Set changed) { - Iterator iter = changed.iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - pw.print(""); - pw.print(key); - pw.println(""); - } - } - -} diff --git a/src/main/zipdiff/package.html b/src/main/zipdiff/package.html deleted file mode 100755 index c615f27..0000000 --- a/src/main/zipdiff/package.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - -Utilities for comparing the contents of 2 different zip files. - - - - diff --git a/src/metadata/JAR-manifest.txt b/src/metadata/JAR-manifest.txt deleted file mode 100644 index 7c72894..0000000 --- a/src/metadata/JAR-manifest.txt +++ /dev/null @@ -1,4 +0,0 @@ -Manifest-Version: 1.0 -Created-By: Sean C. Sullivan -Class-Path: commons-cli-1.0.jar -Main-Class: zipdiff.Main diff --git a/xdocs/sample-output.html b/src/site/sample-output.html similarity index 100% rename from xdocs/sample-output.html rename to src/site/sample-output.html diff --git a/xdocs/navigation.xml b/src/site/site.xml similarity index 100% rename from xdocs/navigation.xml rename to src/site/site.xml diff --git a/xdocs/credits.xml b/src/site/xdoc/credits.xml similarity index 100% rename from xdocs/credits.xml rename to src/site/xdoc/credits.xml diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml new file mode 100644 index 0000000..dbdbcdc --- /dev/null +++ b/src/site/xdoc/index.xml @@ -0,0 +1,86 @@ + + + + + + Welcome + + + + +
+

+ Use the zipdiff tool when you need to compare the contents of two zip files. + It is equally suited for comparing + JAR files, + WAR files, + EAR files or + RAR files. +

+

+ Run it standalone or as an + Ant task. + The tool supports four output formats: plain text, XML, HTML and zip. +

+

+ zipdiff is written in Java +

+

+ The current version is 0.5 +

+
+ +
+ + java -jar zipdiff.jar --source foo.zip --target bar.zip [ --output diffs.html ] [ --comparetimestamps ] [ --comparecrcvalues ] + +
+ +
+ ]]> + ]]> +
+ +
+

+ CLI options and Ant task attributes have the same names when available. +

+
+
source
+
source file, required
+
target
+
target file, required
+
output
+
output file, default stdout; file extension defines output format, default text
+
comparetimestamps
+
compary zip entry timestamps, default false
+
comparecrcvalues
+
compare zip entry CRCs, default false
+
trimsourcelevels
+
remove directory prefixes corresponding to n levels from source zip entry names before comparison, default 0
+
trimtargetlevels
+
remove directory prefixes corresponding to n levels from target zip entry names before comparison, default 0
+
trimoutputlevels
+
remove directory prefixes corresponding to n levels from output zip entry names (only supported for zip output), default 0
+
excluderegex
+
exclude zip entry names matching a regexp from comparison, default none
+
excludecvsfiles
+
exclude CVS control files (zip entry names matching "CVS/") from comparison, default false
+
errorondifference
+
exit with error code if a difference is found (CLI only), default false
+
verbose
+
extra debug messages (CLI only), default false
+
property
+
set a previously unset Ant property to "true" if a difference is found (Ant only)
+
+
+ +
+

+ + SourceForge.net Logo +

+
+ +
diff --git a/xdocs/output.xml b/src/site/xdoc/output.xml similarity index 100% rename from xdocs/output.xml rename to src/site/xdoc/output.xml diff --git a/xdocs/related.xml b/src/site/xdoc/related.xml similarity index 100% rename from xdocs/related.xml rename to src/site/xdoc/related.xml diff --git a/xdocs/tools.xml b/src/site/xdoc/tools.xml similarity index 100% rename from xdocs/tools.xml rename to src/site/xdoc/tools.xml diff --git a/src/test/zipdiff/DifferenceCalculatorTest.java b/src/test/java/zipdiff/DifferenceCalculatorTest.java similarity index 58% rename from src/test/zipdiff/DifferenceCalculatorTest.java rename to src/test/java/zipdiff/DifferenceCalculatorTest.java index a67d793..3ba234e 100755 --- a/src/test/zipdiff/DifferenceCalculatorTest.java +++ b/src/test/java/zipdiff/DifferenceCalculatorTest.java @@ -3,8 +3,11 @@ * * Link: http://www.apache.org/licenses/ */ + package zipdiff; +import static org.junit.Assert.*; + import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -14,8 +17,11 @@ import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; + +import org.junit.BeforeClass; +import org.junit.Test; -import junit.framework.TestCase; import zipdiff.output.AbstractBuilder; import zipdiff.output.HtmlBuilder; import zipdiff.output.TextBuilder; @@ -26,30 +32,66 @@ * * @author jastewart */ -public class DifferenceCalculatorTest extends TestCase { - private static String ENTRYA = "A"; +public class DifferenceCalculatorTest { + /** + * Field ENTRYA. + */ + private static final String ENTRYA = "A"; - private static String ENTRYB = "B"; + /** + * Field ENTRYB. + */ + private static final String ENTRYB = "B"; - private static String ENTRY_CVS = "CVS/Root"; + /** + * Field ENTRY_CVS. + */ + private static final String ENTRY_CVS = "CVS/Root"; + /** + * Field SYSTEM_TMP_DIR_PROPERTY. + * (value is ""java.io.tmpdir"") + */ public static final String SYSTEM_TMP_DIR_PROPERTY = "java.io.tmpdir"; + /** + * Field TEST_DIR_POSTFIX. + * (value is "File.separator + "UnitTestsDifferenceCalculatorTest"") + */ public static final String TEST_DIR_POSTFIX = File.separator + "UnitTestsDifferenceCalculatorTest"; + /** + * Field testDirPathName. + */ private static String testDirPathName; - // naming convention The Capital letter denotes the entry so A will be the same as A - // OneEntry denotes that the jar has one entry + /** Naming conventions:
+ * a Capital letter denotes the entry, so A will be the same as A + * OneEntry denotes that the jar has one entry
+ * Field testJarOneEntryA1Filename. + */ private static String testJarOneEntryA1Filename; + /** + * Field testJarOneEntryA2Filename. + */ private static String testJarOneEntryA2Filename; + /** + * Field testJarOneEntryB1Filename. + */ private static String testJarOneEntryB1Filename; + /** + * Field testJarOneEntryAContentsChangedFilename. + */ private static String testJarOneEntryAContentsChangedFilename; - { + /** + * Method setup. + */ + @BeforeClass + public static void setup() { testDirPathName = System.getProperty(SYSTEM_TMP_DIR_PROPERTY); if (testDirPathName == null) { testDirPathName = File.separator + "temp" + TEST_DIR_POSTFIX; @@ -62,24 +104,26 @@ public class DifferenceCalculatorTest extends TestCase { /** * Create a jar with only one entry in it. That entry being A - * * @throws FileNotFoundException * @throws IOException */ - public void createJarOneEntryA1() throws FileNotFoundException, IOException { - // create a jar file with no duplicates + public void createJarOneEntryA1() throws IOException { + // create a jar file with no duplicates File testDir = new File(testDirPathName); testDir.mkdirs(); - JarOutputStream testJarOS = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(testJarOneEntryA1Filename))); + JarOutputStream testJarOS = + new JarOutputStream( + new BufferedOutputStream( + new FileOutputStream(testJarOneEntryA1Filename))); // ad an entry - JarEntry entry1 = new JarEntry(ENTRYA); - testJarOS.putNextEntry(entry1); - byte data1[] = new byte[2048]; - for (int i = 0; i < data1.length; i++) { - data1[i] = 'a'; + JarEntry entry = new JarEntry(ENTRYA); + testJarOS.putNextEntry(entry); + byte[] data = new byte[2048]; + for (int i = 0; i < data.length; i++) { + data[i] = 'a'; } - testJarOS.write(data1); + testJarOS.write(data); testJarOS.flush(); testJarOS.close(); @@ -87,24 +131,26 @@ public void createJarOneEntryA1() throws FileNotFoundException, IOException { /** * Create a jar with only one entry in it. That entry being A - * * @throws FileNotFoundException * @throws IOException */ - public void createJarOneEntryA2() throws FileNotFoundException, IOException { - // create a jar file with no duplicates + public void createJarOneEntryA2() throws IOException { + // create a jar file with no duplicates File testDir = new File(testDirPathName); testDir.mkdirs(); - JarOutputStream testJarOS = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(testJarOneEntryA2Filename))); + JarOutputStream testJarOS = + new JarOutputStream( + new BufferedOutputStream( + new FileOutputStream(testJarOneEntryA2Filename))); // ad an entry - JarEntry entry1 = new JarEntry(ENTRYA); - testJarOS.putNextEntry(entry1); - byte data1[] = new byte[2048]; - for (int i = 0; i < data1.length; i++) { - data1[i] = 'a'; + JarEntry entry = new JarEntry(ENTRYA); + testJarOS.putNextEntry(entry); + byte[] data = new byte[2048]; + for (int i = 0; i < data.length; i++) { + data[i] = 'a'; } - testJarOS.write(data1); + testJarOS.write(data); testJarOS.flush(); testJarOS.close(); @@ -112,26 +158,28 @@ public void createJarOneEntryA2() throws FileNotFoundException, IOException { /** * Create a jar with only one entry in it. That entry being A - * * @throws FileNotFoundException * @throws IOException */ - public void createJarOneEntryAContentsChanged() throws FileNotFoundException, IOException { - // create a jar file with no duplicates + public void createJarOneEntryAContentsChanged() throws IOException { + // create a jar file with no duplicates File testDir = new File(testDirPathName); testDir.mkdirs(); - JarOutputStream testJarOS = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(testJarOneEntryAContentsChangedFilename))); + JarOutputStream testJarOS = + new JarOutputStream( + new BufferedOutputStream( + new FileOutputStream(testJarOneEntryAContentsChangedFilename))); // add an entry - JarEntry entry1 = new JarEntry(ENTRYA); - testJarOS.putNextEntry(entry1); - byte data1[] = new byte[2048]; - for (int i = 0; i < data1.length; i++) { - data1[i] = 'a'; + JarEntry entry = new JarEntry(ENTRYA); + testJarOS.putNextEntry(entry); + byte[] data = new byte[2048]; + for (int i = 0; i < data.length; i++) { + data[i] = 'a'; } // set a different content so that it will come up as changed - data1[data1.length - 1] = 'b'; - testJarOS.write(data1); + data[data.length - 1] = 'b'; + testJarOS.write(data); // add another entry JarEntry cvs = new JarEntry(ENTRY_CVS); @@ -142,65 +190,71 @@ public void createJarOneEntryAContentsChanged() throws FileNotFoundException, IO } /** - * Create a jar with only one entry in it. That entry being A - * + * Create a jar with only one entry in it. That entry being B * @throws FileNotFoundException * @throws IOException */ - public void createJarOneEntryB1() throws FileNotFoundException, IOException { - // create a jar file with no duplicates + public void createJarOneEntryB1() throws IOException { + // create a jar file with no duplicates File testDir = new File(testDirPathName); testDir.mkdirs(); - JarOutputStream testJarOS = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(testJarOneEntryB1Filename))); + JarOutputStream testJarOS = + new JarOutputStream( + new BufferedOutputStream( + new FileOutputStream(testJarOneEntryB1Filename))); // ad an entry - JarEntry entry1 = new JarEntry(ENTRYB); - testJarOS.putNextEntry(entry1); - byte data1[] = new byte[2048]; - for (int i = 0; i < data1.length; i++) { - data1[i] = 'b'; + JarEntry entry = new JarEntry(ENTRYB); + testJarOS.putNextEntry(entry); + byte[] data = new byte[2048]; + for (int i = 0; i < data.length; i++) { + data[i] = 'b'; } - testJarOS.write(data1); + testJarOS.write(data); testJarOS.flush(); testJarOS.close(); } - /** * Test for Differences calculateDifferences(ZipFile, ZipFile) * with the same file - no differences should be found + * @throws FileNotFoundException + * @throws IOException */ - public void testCalculateDifferencesSameZip() throws FileNotFoundException, IOException { + @Test + public void testCalculateDifferencesSameZip() throws IOException { createJarOneEntryA1(); DifferenceCalculator calc = new DifferenceCalculator(testJarOneEntryA1Filename, testJarOneEntryA1Filename); Differences differences = calc.getDifferences(); assertFalse(differences.hasDifferences()); - Map addedEntries = differences.getAdded(); + Map addedEntries = differences.getAdded(); assertTrue(addedEntries.size() == 0); - Map removedEntries = differences.getRemoved(); + Map removedEntries = differences.getRemoved(); assertTrue(removedEntries.size() == 0); - Map changedEntries = differences.getChanged(); + Map changedEntries = differences.getChanged(); assertTrue(changedEntries.size() == 0); exerciseOutputBuilders(differences); - } /** * Test for Differences calculateDifferences(ZipFile, ZipFile) + * @throws FileNotFoundException + * @throws IOException */ - public void testCalculateDifferencesZipsSameEntries() throws FileNotFoundException, IOException { + @Test + public void testCalculateDifferencesZipsSameEntries() throws IOException { createJarOneEntryA1(); createJarOneEntryA2(); DifferenceCalculator calc = new DifferenceCalculator(testJarOneEntryA1Filename, testJarOneEntryA2Filename); Differences differences = calc.getDifferences(); assertFalse(differences.hasDifferences()); - Map addedEntries = differences.getAdded(); + Map addedEntries = differences.getAdded(); assertTrue(addedEntries.size() == 0); - Map removedEntries = differences.getRemoved(); + Map removedEntries = differences.getRemoved(); assertTrue(removedEntries.size() == 0); - Map changedEntries = differences.getChanged(); + Map changedEntries = differences.getChanged(); assertTrue(changedEntries.size() == 0); exerciseOutputBuilders(differences); @@ -210,48 +264,55 @@ public void testCalculateDifferencesZipsSameEntries() throws FileNotFoundExcepti * Test for Differences calculateDifferences(ZipFile, ZipFile) * Test that the differences between two zips with A in one and B in the second. * A will have been removed and B will have been added. + * @throws FileNotFoundException + * @throws IOException */ - public void testCalculateDifferencesZipsDifferentEntries() throws FileNotFoundException, IOException { + @Test + public void testCalculateDifferencesZipsDifferentEntries() throws IOException { createJarOneEntryA1(); createJarOneEntryB1(); DifferenceCalculator calc = new DifferenceCalculator(testJarOneEntryA1Filename, testJarOneEntryB1Filename); Differences differences = calc.getDifferences(); assertTrue(differences.hasDifferences()); - Map addedEntries = differences.getAdded(); + Map addedEntries = differences.getAdded(); assertTrue(addedEntries.containsKey("B")); - Map removedEntries = differences.getRemoved(); + Map removedEntries = differences.getRemoved(); assertTrue(removedEntries.containsKey("A")); - Map changedEntries = differences.getChanged(); + Map changedEntries = differences.getChanged(); assertTrue(changedEntries.size() == 0); exerciseOutputBuilders(differences); - } - /** * Test for Differences calculateDifferences(ZipFile, ZipFile) * Test that the differences between two zips with A in one and A in the second with different content. * A will have been removed and B will have been added. + * @throws FileNotFoundException + * @throws IOException */ - public void testCalculateDifferencesZipsSameEntriesDifferentContent() throws FileNotFoundException, IOException { + @Test + public void testCalculateDifferencesZipsSameEntriesDifferentContent() throws IOException { createJarOneEntryA1(); createJarOneEntryAContentsChanged(); DifferenceCalculator calc = new DifferenceCalculator(testJarOneEntryA1Filename, testJarOneEntryAContentsChangedFilename); - calc.setIgnoreCVSFiles(true); + calc.setExcludeSCMFiles(true); Differences differences = calc.getDifferences(); assertTrue(differences.hasDifferences()); - Map addedEntries = differences.getAdded(); + Map addedEntries = differences.getAdded(); assertTrue(addedEntries.size() == 0); - Map removedEntries = differences.getRemoved(); + Map removedEntries = differences.getRemoved(); assertTrue(removedEntries.size() == 0); - Map changedEntries = differences.getChanged(); + Map changedEntries = differences.getChanged(); assertTrue(changedEntries.containsKey("A")); exerciseOutputBuilders(differences); - } + /** + * Method exerciseHtmlBuilder. + * @param differences Differences + */ private void exerciseHtmlBuilder(Differences differences) { assertNotNull(differences); @@ -263,6 +324,10 @@ private void exerciseHtmlBuilder(Differences differences) { assertTrue(baos.size() > 0); } + /** + * Method exerciseXmlBuilder. + * @param differences Differences + */ private void exerciseXmlBuilder(Differences differences) { assertNotNull(differences); @@ -274,6 +339,10 @@ private void exerciseXmlBuilder(Differences differences) { assertTrue(baos.size() > 0); } + /** + * Method exerciseTextBuilder. + * @param differences Differences + */ private void exerciseTextBuilder(Differences differences) { assertNotNull(differences); @@ -285,11 +354,14 @@ private void exerciseTextBuilder(Differences differences) { assertTrue(baos.size() > 0); } + /** + * Method exerciseOutputBuilders. + * @param differences Differences + */ private void exerciseOutputBuilders(Differences differences) { assertNotNull(differences); exerciseHtmlBuilder(differences); exerciseXmlBuilder(differences); exerciseTextBuilder(differences); } - } diff --git a/xdocs/index.xml b/xdocs/index.xml deleted file mode 100644 index 475f058..0000000 --- a/xdocs/index.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - Welcome - - - - -
-

- Use the zipdiff tool when you need to compare the contents of two zip files. - It is equally suited for comparing jar files, EAR files, WAR files or RAR files. -

-

- Run it standalone or as an - Ant task. - The tool supports three output formats: plain text, XML, and HTML. -

-

- zipdiff is written in Java -

-

- The current version is 0.4 -

-
- -
- - java -jar zipdiff.jar -file1 foo.zip -file2 bar.zip [ -outputfile diffs.html ] [ -comparetimestamps ] [ -comparecrcvalues ] - -
- -
- -<taskdef name="zipdiff" classname="zipdiff.ant.ZipDiffTask"/> - -<zipdiff filename1="foo.zip" - filename2="foo2.zip" - ignoreTimestamps="true" - compareCRCValues="true" - destfile="zipdiff.xml"> -</zipdiff> - -
- -
-

- - SourceForge.net Logo -

-
- -