aboutsummaryrefslogtreecommitdiffstats
path: root/api/src
diff options
context:
space:
mode:
authorJulien Eluard <[email protected]>2010-11-19 09:57:35 +0100
committerJulien Eluard <[email protected]>2010-11-19 09:57:35 +0100
commitae801320aa1ff89ebdec45559657db5f5841ba11 (patch)
tree8911755cb7269b6f705554910d00f7d3ab5a4f27 /api/src
parent95a44b57760f4a9e656a5bf68421a66095306e97 (diff)
API refactoring.
Diffstat (limited to 'api/src')
-rw-r--r--api/src/main/java/org/semver/Checker.java329
-rw-r--r--api/src/main/java/org/semver/Comparer.java76
-rw-r--r--api/src/main/java/org/semver/Delta.java204
-rw-r--r--api/src/main/java/org/semver/Dumper.java135
-rw-r--r--api/src/main/java/org/semver/Main.java85
-rw-r--r--api/src/main/java/org/semver/Version.java47
-rw-r--r--api/src/main/java/org/semver/jardiff/AccumulatingDiffHandler.java67
-rw-r--r--api/src/test/java/org/semver/VersionTest.java20
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));
- }
-
}