diff options
author | Julien Eluard <[email protected]> | 2010-11-19 09:57:35 +0100 |
---|---|---|
committer | Julien Eluard <[email protected]> | 2010-11-19 09:57:35 +0100 |
commit | ae801320aa1ff89ebdec45559657db5f5841ba11 (patch) | |
tree | 8911755cb7269b6f705554910d00f7d3ab5a4f27 /api/src | |
parent | 95a44b57760f4a9e656a5bf68421a66095306e97 (diff) |
API refactoring.
Diffstat (limited to 'api/src')
-rw-r--r-- | api/src/main/java/org/semver/Checker.java | 329 | ||||
-rw-r--r-- | api/src/main/java/org/semver/Comparer.java | 76 | ||||
-rw-r--r-- | api/src/main/java/org/semver/Delta.java | 204 | ||||
-rw-r--r-- | api/src/main/java/org/semver/Dumper.java | 135 | ||||
-rw-r--r-- | api/src/main/java/org/semver/Main.java | 85 | ||||
-rw-r--r-- | api/src/main/java/org/semver/Version.java | 47 | ||||
-rw-r--r-- | api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java | 67 | ||||
-rw-r--r-- | api/src/test/java/org/semver/VersionTest.java | 20 |
8 files changed, 526 insertions, 437 deletions
diff --git a/api/src/main/java/org/semver/Checker.java b/api/src/main/java/org/semver/Checker.java deleted file mode 100644 index e598327..0000000 --- a/api/src/main/java/org/semver/Checker.java +++ /dev/null @@ -1,329 +0,0 @@ -/** - * This software is licensed under the Apache 2 license, quoted below. - * - * Copyright 2010 Julien Eluard - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * [http://www.apache.org/licenses/LICENSE-2.0] - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.semver; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import javax.annotation.Nonnull; - -import org.osjava.jardiff.AbstractInfo; -import org.osjava.jardiff.ClassInfo; -import org.osjava.jardiff.DiffException; -import org.osjava.jardiff.JarDiff; -import org.osjava.jardiff.SimpleDiffCriteria; -import org.semver.jardiff.AccumulatingDiffHandler; -import org.semver.jardiff.AccumulatingDiffHandler.Difference; - -/** - * - * Allows to compare content of JARs. Provides convenient methods to validate that chosen {@link Version} are correct. - * - */ -public class Checker { - - /** - * Library compatibility type. From most compatible to less compatible. - */ - public enum CompatibilityType { - - BACKWARD_COMPATIBLE_IMPLEMENTER, - - BACKWARD_COMPATIBLE_USER, - - NON_BACKWARD_COMPATIBLE - } - - /** - * - * Infers next {@link Version} depending on provided {@link CompatibilityType}. - * - * @param version - * @param compatibilityType - * @return - */ - public static Version inferNextVersion(@Nonnull final Version version, @Nonnull final CompatibilityType compatibilityType) { - switch (compatibilityType) { - case BACKWARD_COMPATIBLE_IMPLEMENTER: - return version.next(Version.Type.PATCH); - case BACKWARD_COMPATIBLE_USER: - return version.next(Version.Type.MINOR); - case NON_BACKWARD_COMPATIBLE: - return version.next(Version.Type.MAJOR); - default: - throw new IllegalArgumentException("Unknown type <"+compatibilityType+">"); - } - } - - /** - * @param compatibilityType - * @param type - * @return true if specified {@link CompatibilityType} - */ - public static boolean isTypeCompatible(final CompatibilityType compatibilityType, final Version.Type type) { - switch (compatibilityType) { - case BACKWARD_COMPATIBLE_IMPLEMENTER: - return type.isAtLeast(Version.Type.PATCH); - case BACKWARD_COMPATIBLE_USER: - return type.isAtLeast(Version.Type.MINOR); - case NON_BACKWARD_COMPATIBLE: - return type.isAtLeast(Version.Type.MAJOR); - default: - throw new IllegalArgumentException("Unknown type <"+compatibilityType+">"); - } - } - - /** - * @param previousJAR - * @param currentJAR - * @param includes - * @param excludes - * @return all {@link Difference} between both JARs - * @throws IOException - */ - public final Set<AccumulatingDiffHandler.Difference> diff(final File previousJAR, final File currentJAR, final Set<String> includes, final Set<String> excludes) throws IOException { - if (!previousJAR.isFile()) { - throw new IllegalArgumentException("<"+previousJAR+"> is not a valid file"); - } - if (!currentJAR.isFile()) { - throw new IllegalArgumentException("<"+currentJAR+"> is not a valid file"); - } - - try { - final JarDiff jarDiff = new JarDiff(); - jarDiff.loadOldClasses(previousJAR); - jarDiff.loadNewClasses(currentJAR); - final AccumulatingDiffHandler handler = new AccumulatingDiffHandler(includes, excludes); - jarDiff.diff(handler, new SimpleDiffCriteria()); - return handler.getDifferences(); - } catch (DiffException e) { - throw new RuntimeException(e); - } - } - - protected final String extractActionType(final Difference difference) { - final String actionType = difference.getClass().getSimpleName(); - return actionType.endsWith("e")?actionType+"d":actionType+"ed"; - } - - protected final String extractInfoType(final AbstractInfo info) { - final String simpleClassName = info.getClass().getSimpleName(); - return simpleClassName.substring(0, simpleClassName.indexOf("Info")); - } - - protected final String extractDetails(final Difference difference) { - if (difference instanceof AccumulatingDiffHandler.Change) { - final AccumulatingDiffHandler.Change change = (AccumulatingDiffHandler.Change) difference; - return extractDetails(difference.getInfo())+" "+extractAccessDetails(difference.getInfo(), change.getModifiedInfo()); - } else { - return extractDetails(difference.getInfo()); - } - } - - protected final String extractDetails(final AbstractInfo info) { - final StringBuilder builder = new StringBuilder(); - if (!(info instanceof ClassInfo)) { - builder.append(info.getName()); - } - return builder.toString(); - } - - protected final void accumulateAccessDetails(final String access, final boolean previousAccess, final boolean currentAccess, final List<String> added, final List<String> removed) { - if (previousAccess != currentAccess) { - if (previousAccess) { - removed.add(access); - } else { - added.add(access); - } - } - } - - protected final String extractAccessDetails(final AbstractInfo previousInfo, final AbstractInfo currentInfo) { - final List<String> added = new LinkedList<String>(); - final List<String> removed = new LinkedList<String>(); - accumulateAccessDetails("abstract", previousInfo.isAbstract(), currentInfo.isAbstract(), added, removed); - accumulateAccessDetails("annotation", previousInfo.isAnnotation(), currentInfo.isAnnotation(), added, removed); - accumulateAccessDetails("bridge", previousInfo.isBridge(), currentInfo.isBridge(), added, removed); - accumulateAccessDetails("deprecated", previousInfo.isDeprecated(), currentInfo.isDeprecated(), added, removed); - accumulateAccessDetails("enum", previousInfo.isEnum(), currentInfo.isEnum(), added, removed); - accumulateAccessDetails("final", previousInfo.isFinal(), currentInfo.isFinal(), added, removed); - accumulateAccessDetails("interface", previousInfo.isInterface(), currentInfo.isInterface(), added, removed); - accumulateAccessDetails("native", previousInfo.isNative(), currentInfo.isNative(), added, removed); - accumulateAccessDetails("package-private", previousInfo.isPackagePrivate(), currentInfo.isPackagePrivate(), added, removed); - accumulateAccessDetails("private", previousInfo.isPrivate(), currentInfo.isPrivate(), added, removed); - accumulateAccessDetails("protected", previousInfo.isProtected(), currentInfo.isProtected(), added, removed); - accumulateAccessDetails("public", previousInfo.isPublic(), currentInfo.isPublic(), added, removed); - accumulateAccessDetails("static", previousInfo.isStatic(), currentInfo.isStatic(), added, removed); - accumulateAccessDetails("strict", previousInfo.isStrict(), currentInfo.isStrict(), added, removed); - accumulateAccessDetails("super", previousInfo.isSuper(), currentInfo.isSuper(), added, removed); - accumulateAccessDetails("synchronized", previousInfo.isSynchronized(), currentInfo.isSynchronized(), added, removed); - accumulateAccessDetails("synthetic", previousInfo.isSynthetic(), currentInfo.isSynthetic(), added, removed); - accumulateAccessDetails("transcient", previousInfo.isTransient(), currentInfo.isTransient(), added, removed); - accumulateAccessDetails("varargs", previousInfo.isVarargs(), currentInfo.isVarargs(), added, removed); - accumulateAccessDetails("volatile", previousInfo.isVolatile(), currentInfo.isVolatile(), added, removed); - final StringBuilder details = new StringBuilder(); - if (!added.isEmpty()) { - details.append("added: "); - for (final String access : added) { - details.append(access).append(" "); - } - } - if (!removed.isEmpty()) { - details.append("removed: "); - for (final String access : removed) { - details.append(access).append(" "); - } - } - return details.toString().trim(); - } - - /** - * - * Dumps on {@link System#out} all differences between both JARs. - * - * @param previousJAR - * @param currentJAR - * @param includes - * @param excludes - * @throws IOException - */ - public final void dumpDiff(final File previousJAR, final File currentJAR, final Set<String> includes, final Set<String> excludes) throws IOException { - final Set<AccumulatingDiffHandler.Difference> differences = diff(previousJAR, currentJAR, includes, excludes); - final List<AccumulatingDiffHandler.Difference> sortedDifferences = new LinkedList<AccumulatingDiffHandler.Difference>(differences); - Collections.sort(sortedDifferences); - String currentClassName = ""; - for (final AccumulatingDiffHandler.Difference difference : sortedDifferences) { - if (!currentClassName.equals(difference.getClassName())) { - System.out.println("Class "+difference.getClassName()); - } - System.out.println(" "+extractActionType(difference)+" "+extractInfoType(difference.getInfo())+" "+extractDetails(difference)); - currentClassName = difference.getClassName(); - } - } - - protected final boolean contains(final Set<AccumulatingDiffHandler.Difference> differences, final Class<? extends Difference> type) { - for (final Difference difference : differences) { - if (type.isInstance(difference)) { - return false; - } - } - return true; - } - - /** - * @param differences - * @return {@link CompatibilityType} based on specified {@link Difference} - */ - protected final CompatibilityType computeCompatibilityType(final Set<AccumulatingDiffHandler.Difference> differences) { - if (!contains(differences, AccumulatingDiffHandler.Change.class) && - !contains(differences, AccumulatingDiffHandler.Remove.class)) { - return CompatibilityType.NON_BACKWARD_COMPATIBLE; - } else if (!contains(differences, AccumulatingDiffHandler.Add.class)) { - return CompatibilityType.BACKWARD_COMPATIBLE_USER; - } else { - return CompatibilityType.BACKWARD_COMPATIBLE_IMPLEMENTER; - } - } - - public final CompatibilityType check(final File previousJAR, final File currentJAR, final Set<String> includes, final Set<String> excludes) throws IOException { - return computeCompatibilityType(diff(previousJAR, currentJAR, includes, excludes)); - } - - /** - * @param previous - * @param previousJAR - * @param currentJAR - * @param includes - * @param excludes - * @return an inferred {@link Version} for current JAR based on previous JAR content/version. - * @throws IOException - */ - public final Version infer(final Version previous, final File previousJAR, final File currentJAR, final Set<String> includes, final Set<String> excludes) throws IOException { - final CompatibilityType compatibilityType = new Checker().check(previousJAR, currentJAR, includes, excludes); - return Checker.inferNextVersion(previous, compatibilityType); - } - - /** - * @param previous - * @param previousJAR - * @param current - * @param currentJAR - * @param includes - * @param excludes - * @return true if {@link Version} provided for current JAR is compatible with previous JAR content/version. - * @throws IOException - */ - public final boolean validate(final Version previous, final File previousJAR, final Version current, final File currentJAR, final Set<String> includes, final Set<String> excludes) throws IOException { - final CompatibilityType compatibilityType = new Checker().check(previousJAR, currentJAR, includes, excludes); - return isTypeCompatible(compatibilityType, previous.delta(current)); - } - - private static void failIfNotEnoughArguments(final String[] arguments, final int minimalSize, final String message) { - if (arguments.length < minimalSize) { - System.out.println(message); - System.exit(-1); - } - } - - private static final String DIFF_ACTION = "diff"; - private static final String CHECK_ACTION = "check"; - private static final String INFER_ACTION = "infer"; - private static final String VALIDATE_ACTION = "validate"; - - private static Set<String> extractFiltersIfAny(final String[] arguments, final int position) { - try { - final String filters = arguments[position]; - return new HashSet<String>(Arrays.asList(filters.split(";"))); - } catch (IndexOutOfBoundsException e) { - return Collections.emptySet(); - } - } - - public static void main(final String[] arguments) throws IOException { - Checker.failIfNotEnoughArguments(arguments, 3, "Usage: ["+DIFF_ACTION+"|"+CHECK_ACTION+"|"+INFER_ACTION+"|"+VALIDATE_ACTION+"] (previousVersion) previousJar (currentVersion) currentJar (includes) (excludes)"); - - final String action = arguments[0]; - if (DIFF_ACTION.equals(action)) { - Checker.failIfNotEnoughArguments(arguments, 3, "Usage: "+DIFF_ACTION+" previousJar currentJar (includes) (excludes)"); - - new Checker().dumpDiff(new File(arguments[1]), new File(arguments[2]), extractFiltersIfAny(arguments, 3), extractFiltersIfAny(arguments, 4)); - } else if (CHECK_ACTION.equals(action)) { - Checker.failIfNotEnoughArguments(arguments, 3, "Usage: "+CHECK_ACTION+" previousJar currentJar (includes) (excludes)"); - - System.out.println(new Checker().check(new File(arguments[1]), new File(arguments[2]), extractFiltersIfAny(arguments, 3), extractFiltersIfAny(arguments, 4))); - } else if (INFER_ACTION.equals(action)) { - Checker.failIfNotEnoughArguments(arguments, 4, "Usage: "+INFER_ACTION+" previousVersion previousJar currentJar (includes) (excludes)"); - - System.out.println(new Checker().infer(Version.parse(arguments[1]), new File(arguments[2]), new File(arguments[3]), extractFiltersIfAny(arguments, 4), extractFiltersIfAny(arguments, 5))); - } else if (VALIDATE_ACTION.equals(action)) { - Checker.failIfNotEnoughArguments(arguments, 5, "Usage: "+VALIDATE_ACTION+" previousVersion previousJar currentVersion currentJar (includes) (excludes)"); - - System.out.println(new Checker().validate(Version.parse(arguments[1]), new File(arguments[2]), Version.parse(arguments[3]), new File(arguments[4]), extractFiltersIfAny(arguments, 5), extractFiltersIfAny(arguments, 6))); - } else { - System.out.println("First argument must be one of ["+DIFF_ACTION+"|"+CHECK_ACTION+"|"+INFER_ACTION+"|"+VALIDATE_ACTION+"]"); - System.exit(-1); - } - } - -} diff --git a/api/src/main/java/org/semver/Comparer.java b/api/src/main/java/org/semver/Comparer.java new file mode 100644 index 0000000..9182960 --- /dev/null +++ b/api/src/main/java/org/semver/Comparer.java @@ -0,0 +1,76 @@ +/** + * This software is licensed under the Apache 2 license, quoted below. + * + * Copyright 2010 Julien Eluard + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * [http://www.apache.org/licenses/LICENSE-2.0] + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.semver; + +import java.io.File; +import java.io.IOException; +import java.util.Set; +import javax.annotation.concurrent.NotThreadSafe; + +import org.osjava.jardiff.DiffException; +import org.osjava.jardiff.JarDiff; +import org.osjava.jardiff.SimpleDiffCriteria; +import org.semver.jardiff.AccumulatingDiffHandler; +import org.semver.Delta.Difference; + +/** + * + * Allows to compare content of JARs. + * + */ +@NotThreadSafe +public class Comparer { + + private final File previousJAR; + private final File currentJAR; + private final Set<String> includes; + private final Set<String> excludes; + + public Comparer(final File previousJAR, final File currentJAR, final Set<String> includes, final Set<String> excludes) { + if (!previousJAR.isFile()) { + throw new IllegalArgumentException("<"+previousJAR+"> is not a valid file"); + } + if (!currentJAR.isFile()) { + throw new IllegalArgumentException("<"+currentJAR+"> is not a valid file"); + } + + this.previousJAR = previousJAR; + this.currentJAR = currentJAR; + this.includes = includes; + this.excludes = excludes; + } + + /** + * @return all {@link Difference} between both JARs + * @throws IOException + */ + public final Delta diff() throws IOException { + try { + final JarDiff jarDiff = new JarDiff(); + jarDiff.loadOldClasses(this.previousJAR); + jarDiff.loadNewClasses(this.currentJAR); + final AccumulatingDiffHandler handler = new AccumulatingDiffHandler(this.includes, this.excludes); + jarDiff.diff(handler, new SimpleDiffCriteria()); + return handler.getDelta(); + } catch (DiffException e) { + throw new RuntimeException(e); + } + } + +}
\ No newline at end of file diff --git a/api/src/main/java/org/semver/Delta.java b/api/src/main/java/org/semver/Delta.java new file mode 100644 index 0000000..6fdb9a3 --- /dev/null +++ b/api/src/main/java/org/semver/Delta.java @@ -0,0 +1,204 @@ +/** + * This software is licensed under the Apache 2 license, quoted below. + * + * Copyright 2010 Julien Eluard + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * [http://www.apache.org/licenses/LICENSE-2.0] + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.semver; + +import java.util.Collections; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; + +import org.osjava.jardiff.AbstractInfo; + +/** + * + * Encapsulates differences between two sets of classes. + * <br /> + * Provides convenient methods to validate that chosen {@link Version} are correct. + * + */ +@Immutable +public class Delta { + + /** + * Library compatibility type. From most compatible to less compatible. + */ + public enum CompatibilityType { + + BACKWARD_COMPATIBLE_IMPLEMENTER, + + BACKWARD_COMPATIBLE_USER, + + NON_BACKWARD_COMPATIBLE + } + + public static class Difference implements Comparable<Difference> { + + private final String className; + private final AbstractInfo info; + + public Difference(@Nonnull final String className, @Nonnull final AbstractInfo info) { + this.className = className; + this.info = info; + } + + public String getClassName() { + return this.className; + } + + public AbstractInfo getInfo() { + return info; + } + + @Override + public int compareTo(final Difference other) { + return getClassName().compareTo(other.getClassName()); + } + + } + + public static class Add extends Difference { + + public Add(@Nonnull final String className, @Nonnull final AbstractInfo info) { + super(className, info); + } + + } + + public static class Change extends Difference { + + private final AbstractInfo modifiedInfo; + + public Change(@Nonnull final String className, @Nonnull final AbstractInfo info, @Nonnull final AbstractInfo modifiedInfo) { + super(className, info); + + this.modifiedInfo = modifiedInfo; + } + + public AbstractInfo getModifiedInfo() { + return this.modifiedInfo; + } + + } + + public static class Remove extends Difference { + + public Remove(@Nonnull final String className, @Nonnull final AbstractInfo info) { + super(className, info); + } + + } + + private final Set<Difference> differences; + + public Delta(@Nonnull final Set<Difference> differences) { + this.differences = Collections.unmodifiableSet(this.differences); + } + + public Set<Difference> getDifferences() { + return this.differences; + } + + /** + * @param differences + * @return {@link CompatibilityType} based on specified {@link Difference} + */ + public final CompatibilityType computeCompatibilityType() { + if (!contains(this.differences, Change.class) && + !contains(this.differences, Remove.class)) { + return CompatibilityType.NON_BACKWARD_COMPATIBLE; + } else if (!contains(this.differences, Add.class)) { + return CompatibilityType.BACKWARD_COMPATIBLE_USER; + } else { + return CompatibilityType.BACKWARD_COMPATIBLE_IMPLEMENTER; + } + } + + protected final boolean contains(final Set<Difference> differences, final Class<? extends Difference> type) { + for (final Difference difference : differences) { + if (type.isInstance(difference)) { + return false; + } + } + return true; + } + + /** + * + * Infers next {@link Version} depending on provided {@link CompatibilityType}. + * + * @param version + * @param compatibilityType + * @return + */ + public static Version inferNextVersion(@Nonnull final Version version, @Nonnull final CompatibilityType compatibilityType) { + switch (compatibilityType) { + case BACKWARD_COMPATIBLE_IMPLEMENTER: + return version.next(Version.Element.PATCH); + case BACKWARD_COMPATIBLE_USER: + return version.next(Version.Element.MINOR); + case NON_BACKWARD_COMPATIBLE: + return version.next(Version.Element.MAJOR); + default: + throw new IllegalArgumentException("Unknown type <"+compatibilityType+">"); + } + } + + /** + * @param previous + * @param previousJAR + * @param currentJAR + * @param includes + * @param excludes + * @return an inferred {@link Version} for current JAR based on previous JAR content/version. + * @throws IOException + */ + public final Version infer(final Version previous) { + if (previous.isInDevelopment()) { + throw new IllegalArgumentException("Cannot infer for in development version <"+previous+">"); + } + + final CompatibilityType compatibilityType = computeCompatibilityType(); + return inferNextVersion(previous, compatibilityType); + } + + /** + * @param previous + * @param previousJAR + * @param current + * @param currentJAR + * @param includes + * @param excludes + * @return true if {@link Version} provided for current JAR is compatible with previous JAR content/version. + * @throws IOException + */ + public final boolean validate(final Version previous, final Version current) { + if (previous.compareTo(current) < 0) { + throw new IllegalArgumentException("Previous version <"+previous+"> must not be more recent than current version <"+current+">."); + } + //When in development public API is not considered stable + if (current.isInDevelopment()) { + return true; + } + + //Current version must be superior or equals to inferred version + final Version inferredVersion = infer(previous); + return inferredVersion.compareTo(current) >= 0; + } + +} diff --git a/api/src/main/java/org/semver/Dumper.java b/api/src/main/java/org/semver/Dumper.java new file mode 100644 index 0000000..e2dd513 --- /dev/null +++ b/api/src/main/java/org/semver/Dumper.java @@ -0,0 +1,135 @@ +/** + * This software is licensed under the Apache 2 license, quoted below. + * + * Copyright 2010 Julien Eluard + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * [http://www.apache.org/licenses/LICENSE-2.0] + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.semver; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.osjava.jardiff.AbstractInfo; +import org.osjava.jardiff.ClassInfo; +import org.semver.Delta.Difference; +import org.semver.Delta.Change; + +/** + * + * Helper methods to dump {@link Delta}. + * + */ +public final class Dumper { + + private Dumper() { + } + + protected static String extractActionType(final Difference difference) { + final String actionType = difference.getClass().getSimpleName(); + return actionType.endsWith("e")?actionType+"d":actionType+"ed"; + } + + protected static String extractInfoType(final AbstractInfo info) { + final String simpleClassName = info.getClass().getSimpleName(); + return simpleClassName.substring(0, simpleClassName.indexOf("Info")); + } + + protected static String extractDetails(final Difference difference) { + if (difference instanceof Change) { + final Change change = (Change) difference; + return extractDetails(difference.getInfo())+" "+extractAccessDetails(difference.getInfo(), change.getModifiedInfo()); + } else { + return extractDetails(difference.getInfo()); + } + } + + protected static String extractDetails(final AbstractInfo info) { + final StringBuilder builder = new StringBuilder(); + if (!(info instanceof ClassInfo)) { + builder.append(info.getName()); + } + return builder.toString(); + } + + protected static void accumulateAccessDetails(final String access, final boolean previousAccess, final boolean currentAccess, final List<String> added, final List<String> removed) { + if (previousAccess != currentAccess) { + if (previousAccess) { + removed.add(access); + } else { + added.add(access); + } + } + } + + protected static String extractAccessDetails(final AbstractInfo previousInfo, final AbstractInfo currentInfo) { + final List<String> added = new LinkedList<String>(); + final List<String> removed = new LinkedList<String>(); + accumulateAccessDetails("abstract", previousInfo.isAbstract(), currentInfo.isAbstract(), added, removed); + accumulateAccessDetails("annotation", previousInfo.isAnnotation(), currentInfo.isAnnotation(), added, removed); + accumulateAccessDetails("bridge", previousInfo.isBridge(), currentInfo.isBridge(), added, removed); + accumulateAccessDetails("deprecated", previousInfo.isDeprecated(), currentInfo.isDeprecated(), added, removed); + accumulateAccessDetails("enum", previousInfo.isEnum(), currentInfo.isEnum(), added, removed); + accumulateAccessDetails("final", previousInfo.isFinal(), currentInfo.isFinal(), added, removed); + accumulateAccessDetails("interface", previousInfo.isInterface(), currentInfo.isInterface(), added, removed); + accumulateAccessDetails("native", previousInfo.isNative(), currentInfo.isNative(), added, removed); + accumulateAccessDetails("package-private", previousInfo.isPackagePrivate(), currentInfo.isPackagePrivate(), added, removed); + accumulateAccessDetails("private", previousInfo.isPrivate(), currentInfo.isPrivate(), added, removed); + accumulateAccessDetails("protected", previousInfo.isProtected(), currentInfo.isProtected(), added, removed); + accumulateAccessDetails("public", previousInfo.isPublic(), currentInfo.isPublic(), added, removed); + accumulateAccessDetails("static", previousInfo.isStatic(), currentInfo.isStatic(), added, removed); + accumulateAccessDetails("strict", previousInfo.isStrict(), currentInfo.isStrict(), added, removed); + accumulateAccessDetails("super", previousInfo.isSuper(), currentInfo.isSuper(), added, removed); + accumulateAccessDetails("synchronized", previousInfo.isSynchronized(), currentInfo.isSynchronized(), added, removed); + accumulateAccessDetails("synthetic", previousInfo.isSynthetic(), currentInfo.isSynthetic(), added, removed); + accumulateAccessDetails("transcient", previousInfo.isTransient(), currentInfo.isTransient(), added, removed); + accumulateAccessDetails("varargs", previousInfo.isVarargs(), currentInfo.isVarargs(), added, removed); + accumulateAccessDetails("volatile", previousInfo.isVolatile(), currentInfo.isVolatile(), added, removed); + final StringBuilder details = new StringBuilder(); + if (!added.isEmpty()) { + details.append("added: "); + for (final String access : added) { + details.append(access).append(" "); + } + } + if (!removed.isEmpty()) { + details.append("removed: "); + for (final String access : removed) { + details.append(access).append(" "); + } + } + return details.toString().trim(); + } + + /** + * + * Dumps on {@link System#out} all differences. + * + * @param differences + */ + public static void dump(final Delta delta) { + final List<Difference> sortedDifferences = new LinkedList<Difference>(delta.getDifferences()); + Collections.sort(sortedDifferences); + String currentClassName = ""; + for (final Difference difference : sortedDifferences) { + if (!currentClassName.equals(difference.getClassName())) { + System.out.println("Class "+difference.getClassName()); + } + System.out.println(" "+extractActionType(difference)+" "+extractInfoType(difference.getInfo())+" "+extractDetails(difference)); + currentClassName = difference.getClassName(); + } + } + +} diff --git a/api/src/main/java/org/semver/Main.java b/api/src/main/java/org/semver/Main.java new file mode 100644 index 0000000..ec2f8e7 --- /dev/null +++ b/api/src/main/java/org/semver/Main.java @@ -0,0 +1,85 @@ +/** + * This software is licensed under the Apache 2 license, quoted below. + * + * Copyright 2010 Julien Eluard + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * [http://www.apache.org/licenses/LICENSE-2.0] + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.semver; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * + * CLI interface. + * + */ +public class Main { + + private static void failIfNotEnoughArguments(final String[] arguments, final int minimalSize, final String message) { + if (arguments.length < minimalSize) { + System.out.println(message); + System.exit(-1); + } + } + + private static final String DIFF_ACTION = "diff"; + private static final String CHECK_ACTION = "check"; + private static final String INFER_ACTION = "infer"; + private static final String VALIDATE_ACTION = "validate"; + + private static Set<String> extractFiltersIfAny(final String[] arguments, final int position) { + try { + final String filters = arguments[position]; + return new HashSet<String>(Arrays.asList(filters.split(";"))); + } catch (IndexOutOfBoundsException e) { + return Collections.emptySet(); + } + } + + public static void main(final String[] arguments) throws IOException { + Main.failIfNotEnoughArguments(arguments, 3, "Usage: ["+DIFF_ACTION+"|"+CHECK_ACTION+"|"+INFER_ACTION+"|"+VALIDATE_ACTION+"] (previousVersion) previousJar (currentVersion) currentJar (includes) (excludes)"); + + final String action = arguments[0]; + if (DIFF_ACTION.equals(action)) { + final Comparer comparer = new Comparer(new File(arguments[1]), new File(arguments[2]), extractFiltersIfAny(arguments, 3), extractFiltersIfAny(arguments, 4)); + Dumper.dump(comparer.diff()); + } else if (CHECK_ACTION.equals(action)) { + final Comparer comparer = new Comparer(new File(arguments[1]), new File(arguments[2]), extractFiltersIfAny(arguments, 3), extractFiltersIfAny(arguments, 4)); + final Delta delta = comparer.diff(); + System.out.println(delta.computeCompatibilityType()); + } else if (INFER_ACTION.equals(action)) { + Main.failIfNotEnoughArguments(arguments, 4, "Usage: "+INFER_ACTION+" previousVersion previousJar currentJar (includes) (excludes)"); + + final Comparer comparer = new Comparer(new File(arguments[2]), new File(arguments[3]), extractFiltersIfAny(arguments, 4), extractFiltersIfAny(arguments, 5)); + final Delta delta = comparer.diff(); + System.out.println(delta.infer(Version.parse(arguments[1]))); + } else if (VALIDATE_ACTION.equals(action)) { + Main.failIfNotEnoughArguments(arguments, 5, "Usage: "+VALIDATE_ACTION+" previousVersion previousJar currentVersion currentJar (includes) (excludes)"); + + final Comparer comparer = new Comparer(new File(arguments[2]), new File(arguments[4]), extractFiltersIfAny(arguments, 5), extractFiltersIfAny(arguments, 6)); + final Delta delta = comparer.diff(); + System.out.println(delta.validate(Version.parse(arguments[1]), Version.parse(arguments[3]))); + } else { + System.out.println("First argument must be one of ["+DIFF_ACTION+"|"+CHECK_ACTION+"|"+INFER_ACTION+"|"+VALIDATE_ACTION+"]"); + System.exit(-1); + } + } + +} diff --git a/api/src/main/java/org/semver/Version.java b/api/src/main/java/org/semver/Version.java index a138b49..62e5da5 100644 --- a/api/src/main/java/org/semver/Version.java +++ b/api/src/main/java/org/semver/Version.java @@ -23,24 +23,21 @@ import java.util.regex.Pattern; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; /** * * Version following semantic defined by <a href="http://semver.org/">Semantic Versioning</a> document. * */ +@Immutable public final class Version implements Comparable<Version> { /** - * {@link Version} element type. From most meaningful to less meaningful. + * {@link Version} element. From most meaningful to less meaningful. */ - public enum Type { + public enum Element { MAJOR, MINOR, PATCH, SPECIAL; - - public boolean isAtLeast(@Nonnull final Version.Type type) { - return compareTo(type) <= 0; - } - } private static final String FORMAT = "(\\d)\\.(\\d)\\.(\\d)([A-Za-z][0-9A-Za-z-]*)?"; @@ -57,13 +54,13 @@ public final class Version implements Comparable<Version> { public Version(@Nonnegative final int major, @Nonnegative final int minor, @Nonnegative final int patch, @Nonnull final String special) { if (major < 0) { - throw new IllegalArgumentException(Type.MAJOR+" must be positive"); + throw new IllegalArgumentException(Element.MAJOR+" must be positive"); } if (minor < 0) { - throw new IllegalArgumentException(Type.MINOR+" must be positive"); + throw new IllegalArgumentException(Element.MINOR+" must be positive"); } if (patch < 0) { - throw new IllegalArgumentException(Type.PATCH+" must be positive"); + throw new IllegalArgumentException(Element.PATCH+" must be positive"); } this.major = major; @@ -85,9 +82,9 @@ public final class Version implements Comparable<Version> { throw new IllegalArgumentException("<"+version+"> does not match format "+Version.FORMAT); } - final int major = Version.parseElement(matcher.group(1), Type.MAJOR); - final int minor = Version.parseElement(matcher.group(2), Type.MINOR); - final int patch = Version.parseElement(matcher.group(3), Type.PATCH); + final int major = Version.parseElement(matcher.group(1), Element.MAJOR); + final int minor = Version.parseElement(matcher.group(2), Element.MINOR); + final int patch = Version.parseElement(matcher.group(3), Element.PATCH); if (matcher.groupCount() == 4) { return new Version(major, minor, patch, matcher.group(4)); @@ -101,7 +98,7 @@ public final class Version implements Comparable<Version> { * @param type * @return int representation of provided number */ - private @Nonnegative static int parseElement(@Nonnull final String number, @Nonnull final Version.Type type) { + private @Nonnegative static int parseElement(@Nonnull final String number, @Nonnull final Version.Element type) { try { return Integer.valueOf(number); } catch (NumberFormatException e) { @@ -111,9 +108,9 @@ public final class Version implements Comparable<Version> { /** * @param type - * @return next {@link Version} regarding specified {@link Version.Type} + * @return next {@link Version} regarding specified {@link Version.Element} */ - public Version next(@Nonnull final Version.Type type) { + public Version next(@Nonnull final Version.Element type) { switch (type) { case MAJOR: return new Version(this.major+1, 0, 0); @@ -126,24 +123,6 @@ public final class Version implements Comparable<Version> { } } - /** - * @param other - * @return most important differing {@link Version.Type} component between this and another {@link Version}, null if both are same. - */ - public Version.Type delta(@Nonnull final Version other) { - if (this.major != other.major) { - return Version.Type.MAJOR; - } else if (this.minor != other.minor) { - return Version.Type.MINOR; - } else if (this.patch != other.patch) { - return Version.Type.PATCH; - } else if (this.special.equals(other.special)) { - return null; - } else { - return null; - } - } - public boolean isInDevelopment() { return this.major == 0; } diff --git a/api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java b/api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java index 3ca479d..a0582de 100644 --- a/api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java +++ b/api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java @@ -24,11 +24,15 @@ import java.util.Set; import javax.annotation.Nonnull; import org.osjava.jardiff.AbstractDiffHandler; -import org.osjava.jardiff.AbstractInfo; import org.osjava.jardiff.ClassInfo; import org.osjava.jardiff.DiffException; import org.osjava.jardiff.FieldInfo; import org.osjava.jardiff.MethodInfo; +import org.semver.Delta; +import org.semver.Delta.Add; +import org.semver.Delta.Change; +import org.semver.Delta.Difference; +import org.semver.Delta.Remove; /** * @@ -36,63 +40,6 @@ import org.osjava.jardiff.MethodInfo; * */ public final class AccumulatingDiffHandler extends AbstractDiffHandler { - - public static class Difference implements Comparable<Difference> { - - private final String className; - private final AbstractInfo info; - - public Difference(@Nonnull final String className, @Nonnull final AbstractInfo info) { - this.className = className; - this.info = info; - } - - public String getClassName() { - return this.className; - } - - public AbstractInfo getInfo() { - return info; - } - - @Override - public int compareTo(final Difference other) { - return getClassName().compareTo(other.getClassName()); - } - - } - - public static class Add extends Difference { - - public Add(@Nonnull final String className, @Nonnull final AbstractInfo info) { - super(className, info); - } - - } - - public static class Change extends Difference { - - private final AbstractInfo modifiedInfo; - - public Change(@Nonnull final String className, @Nonnull final AbstractInfo info, @Nonnull final AbstractInfo modifiedInfo) { - super(className, info); - - this.modifiedInfo = modifiedInfo; - } - - public AbstractInfo getModifiedInfo() { - return this.modifiedInfo; - } - - } - - public class Remove extends Difference { - - public Remove(@Nonnull final String className, @Nonnull final AbstractInfo info) { - super(className, info); - } - - } private String currentClassName; private final Set<String> includes; @@ -278,8 +225,8 @@ public final class AccumulatingDiffHandler extends AbstractDiffHandler { return true; } - public Set<Difference> getDifferences() { - return Collections.unmodifiableSet(this.differences); + public Delta getDelta() { + return new Delta(this.differences); } } diff --git a/api/src/test/java/org/semver/VersionTest.java b/api/src/test/java/org/semver/VersionTest.java index 4197f8a..73f3b76 100644 --- a/api/src/test/java/org/semver/VersionTest.java +++ b/api/src/test/java/org/semver/VersionTest.java @@ -43,7 +43,7 @@ public class VersionTest { } @Test - public void shouldValidVersionAreParsed() { + public void shouldValidVersionBeParsed() { Version.parse("1.2.3"); Version.parse("1.2.3beta"); } @@ -63,6 +63,11 @@ public class VersionTest { Version.parse("1.2.3.beta"); } + @Test(expected=IllegalArgumentException.class) + public void shouldInvalidVersion4NotBeParsed() { + Version.parse("1.2"); + } + @Test public void shouldDevelopmentBeInDevelopment() { Assert.assertTrue(Version.parse("0.1.1").isInDevelopment()); @@ -85,17 +90,4 @@ public class VersionTest { Assert.assertFalse(Version.parse("0.0.0").compareTo(Version.parse("0.0.1")) < 0); } - @Test - public void isAtLeast() { - Assert.assertTrue(Version.Type.MAJOR.isAtLeast(Version.Type.MAJOR)); - Assert.assertTrue(Version.Type.MAJOR.isAtLeast(Version.Type.MINOR)); - Assert.assertTrue(Version.Type.MAJOR.isAtLeast(Version.Type.PATCH)); - Assert.assertFalse(Version.Type.MINOR.isAtLeast(Version.Type.MAJOR)); - Assert.assertTrue(Version.Type.MINOR.isAtLeast(Version.Type.MINOR)); - Assert.assertTrue(Version.Type.MINOR.isAtLeast(Version.Type.PATCH)); - Assert.assertFalse(Version.Type.PATCH.isAtLeast(Version.Type.MAJOR)); - Assert.assertFalse(Version.Type.PATCH.isAtLeast(Version.Type.MINOR)); - Assert.assertTrue(Version.Type.PATCH.isAtLeast(Version.Type.PATCH)); - } - } |