2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.ivy.plugins.latest;
20
import java.util.Comparator;
21
import java.util.HashMap;
22
import java.util.Locale;
25
import org.apache.ivy.core.IvyContext;
26
import org.apache.ivy.core.module.id.ModuleRevisionId;
27
import org.apache.ivy.plugins.version.VersionMatcher;
29
public class LatestRevisionStrategy extends ComparatorLatestStrategy {
31
* Compares two ModuleRevisionId by their revision. Revisions are compared using an algorithm
32
* inspired by PHP version_compare one.
34
final class MridComparator implements Comparator {
35
public int compare(Object o1, Object o2) {
36
String rev1 = ((ModuleRevisionId) o1).getRevision();
37
String rev2 = ((ModuleRevisionId) o2).getRevision();
39
rev1 = rev1.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
40
rev1 = rev1.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
41
rev2 = rev2.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
42
rev2 = rev2.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
44
String[] parts1 = rev1.split("[\\._\\-\\+]");
45
String[] parts2 = rev2.split("[\\._\\-\\+]");
48
for (; i < parts1.length && i < parts2.length; i++) {
49
if (parts1[i].equals(parts2[i])) {
52
boolean is1Number = isNumber(parts1[i]);
53
boolean is2Number = isNumber(parts2[i]);
54
if (is1Number && !is2Number) {
57
if (is2Number && !is1Number) {
60
if (is1Number && is2Number) {
61
return Long.valueOf(parts1[i]).compareTo(Long.valueOf(parts2[i]));
63
// both are strings, we compare them taking into account special meaning
64
Map specialMeanings = getSpecialMeanings();
65
Integer sm1 = (Integer) specialMeanings.get(parts1[i].toLowerCase(Locale.US));
66
Integer sm2 = (Integer) specialMeanings.get(parts2[i].toLowerCase(Locale.US));
68
sm2 = sm2 == null ? new Integer(0) : sm2;
69
return sm1.compareTo(sm2);
72
return new Integer(0).compareTo(sm2);
74
return parts1[i].compareTo(parts2[i]);
76
if (i < parts1.length) {
77
return isNumber(parts1[i]) ? 1 : -1;
79
if (i < parts2.length) {
80
return isNumber(parts2[i]) ? -1 : 1;
85
private boolean isNumber(String str) {
86
return str.matches("\\d+");
91
* Compares two ArtifactInfo by their revision. Revisions are compared using an algorithm
92
* inspired by PHP version_compare one, unless a dynamic revision is given, in which case the
93
* version matcher is used to perform the comparison.
95
final class ArtifactInfoComparator implements Comparator {
96
public int compare(Object o1, Object o2) {
97
String rev1 = ((ArtifactInfo) o1).getRevision();
98
String rev2 = ((ArtifactInfo) o2).getRevision();
101
* The revisions can still be not resolved, so we use the current version matcher to
102
* know if one revision is dynamic, and in this case if it should be considered greater
103
* or lower than the other one. Note that if the version matcher compare method returns
104
* 0, it's because it's not possible to know which revision is greater. In this case we
105
* consider the dynamic one to be greater, because most of the time it will then be
106
* actually resolved and a real comparison will occur.
108
VersionMatcher vmatcher = IvyContext.getContext().getSettings().getVersionMatcher();
109
ModuleRevisionId mrid1 = ModuleRevisionId.newInstance("", "", rev1);
110
ModuleRevisionId mrid2 = ModuleRevisionId.newInstance("", "", rev2);
111
if (vmatcher.isDynamic(mrid1)) {
112
int c = vmatcher.compare(mrid1, mrid2, mridComparator);
113
return c >= 0 ? 1 : -1;
114
} else if (vmatcher.isDynamic(mrid2)) {
115
int c = vmatcher.compare(mrid2, mrid1, mridComparator);
116
return c >= 0 ? -1 : 1;
119
return mridComparator.compare(mrid1, mrid2);
123
public static class SpecialMeaning {
126
private Integer value;
128
public String getName() {
132
public void setName(String name) {
136
public Integer getValue() {
140
public void setValue(Integer value) {
144
public void validate() {
146
throw new IllegalStateException("a special meaning should have a name");
149
throw new IllegalStateException("a special meaning should have a value");
154
private static final Map DEFAULT_SPECIAL_MEANINGS;
156
DEFAULT_SPECIAL_MEANINGS = new HashMap();
157
DEFAULT_SPECIAL_MEANINGS.put("dev", new Integer(-1));
158
DEFAULT_SPECIAL_MEANINGS.put("rc", new Integer(1));
159
DEFAULT_SPECIAL_MEANINGS.put("final", new Integer(2));
162
private final Comparator mridComparator = new MridComparator();
164
private final Comparator artifactInfoComparator = new ArtifactInfoComparator();
166
private Map specialMeanings = null;
168
private boolean usedefaultspecialmeanings = true;
170
public LatestRevisionStrategy() {
171
setComparator(artifactInfoComparator);
172
setName("latest-revision");
175
public void addConfiguredSpecialMeaning(SpecialMeaning meaning) {
177
getSpecialMeanings().put(meaning.getName().toLowerCase(Locale.US), meaning.getValue());
180
public synchronized Map getSpecialMeanings() {
181
if (specialMeanings == null) {
182
specialMeanings = new HashMap();
183
if (isUsedefaultspecialmeanings()) {
184
specialMeanings.putAll(DEFAULT_SPECIAL_MEANINGS);
187
return specialMeanings;
190
public boolean isUsedefaultspecialmeanings() {
191
return usedefaultspecialmeanings;
194
public void setUsedefaultspecialmeanings(boolean usedefaultspecialmeanings) {
195
this.usedefaultspecialmeanings = usedefaultspecialmeanings;