| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 |
| * |
| * https://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.apache.ivy.plugins.version; |
| |
| import java.util.Comparator; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.ivy.core.module.id.ModuleRevisionId; |
| import org.apache.ivy.plugins.latest.ArtifactInfo; |
| import org.apache.ivy.plugins.latest.LatestStrategy; |
| |
| /** |
| * Matches version ranges: [1.0,2.0] matches all versions greater or equal to 1.0 and lower or equal |
| * to 2.0 [1.0,2.0[ matches all versions greater or equal to 1.0 and lower than 2.0 ]1.0,2.0] |
| * matches all versions greater than 1.0 and lower or equal to 2.0 ]1.0,2.0[ matches all versions |
| * greater than 1.0 and lower than 2.0 [1.0,) matches all versions greater or equal to 1.0 ]1.0,) |
| * matches all versions greater than 1.0 (,2.0] matches all versions lower or equal to 2.0 (,2.0[ |
| * matches all versions lower than 2.0 This class uses a latest strategy to compare revisions. If |
| * none is set, it uses the default one of the ivy instance set through setIvy(). If neither a |
| * latest strategy nor a ivy instance is set, an IllegalStateException will be thrown when calling |
| * accept(). Note that it can't work with latest time strategy, cause no time is known for the |
| * limits of the range. Therefore only purely revision based LatestStrategy can be used. |
| */ |
| public class VersionRangeMatcher extends AbstractVersionMatcher { |
| // todo: check these constants |
| private static final String OPEN_INC = "["; |
| |
| private static final String OPEN_EXC = "]"; |
| |
| private static final String OPEN_EXC_MAVEN = "("; |
| |
| private static final String CLOSE_INC = "]"; |
| |
| private static final String CLOSE_EXC = "["; |
| |
| private static final String CLOSE_EXC_MAVEN = ")"; |
| |
| private static final String LOWER_INFINITE = "("; |
| |
| private static final String UPPER_INFINITE = ")"; |
| |
| private static final String SEPARATOR = ","; |
| |
| // following patterns are built upon constants above and should not be modified |
| private static final String OPEN_INC_PATTERN = "\\" + OPEN_INC; |
| |
| private static final String OPEN_EXC_PATTERN = "\\" + OPEN_EXC + "\\" + OPEN_EXC_MAVEN; |
| |
| private static final String CLOSE_INC_PATTERN = "\\" + CLOSE_INC; |
| |
| private static final String CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC + "\\" + CLOSE_EXC_MAVEN; |
| |
| private static final String LI_PATTERN = "\\" + LOWER_INFINITE; |
| |
| private static final String UI_PATTERN = "\\" + UPPER_INFINITE; |
| |
| private static final String SEP_PATTERN = "\\s*\\" + SEPARATOR + "\\s*"; |
| |
| private static final String OPEN_PATTERN = "[" + OPEN_INC_PATTERN + OPEN_EXC_PATTERN + "]"; |
| |
| private static final String CLOSE_PATTERN = "[" + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + "]"; |
| |
| private static final String ANY_NON_SPECIAL_PATTERN = "[^\\s" + SEPARATOR + OPEN_INC_PATTERN |
| + OPEN_EXC_PATTERN + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN + UI_PATTERN |
| + "]"; |
| |
| private static final String FINITE_PATTERN = OPEN_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN |
| + "+)" + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN; |
| |
| private static final String LOWER_INFINITE_PATTERN = LI_PATTERN + SEP_PATTERN + "(" |
| + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN; |
| |
| private static final String UPPER_INFINITE_PATTERN = OPEN_PATTERN + "\\s*(" |
| + ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + UI_PATTERN; |
| |
| private static final Pattern FINITE_RANGE = Pattern.compile(FINITE_PATTERN); |
| |
| private static final Pattern LOWER_INFINITE_RANGE = Pattern.compile(LOWER_INFINITE_PATTERN); |
| |
| private static final Pattern UPPER_INFINITE_RANGE = Pattern.compile(UPPER_INFINITE_PATTERN); |
| |
| private static final Pattern ALL_RANGE = Pattern.compile(FINITE_PATTERN + "|" |
| + LOWER_INFINITE_PATTERN + "|" + UPPER_INFINITE_PATTERN); |
| |
| private final class MRIDArtifactInfo implements ArtifactInfo { |
| private ModuleRevisionId mrid; |
| |
| public MRIDArtifactInfo(ModuleRevisionId id) { |
| mrid = id; |
| } |
| |
| public long getLastModified() { |
| return 0; |
| } |
| |
| public String getRevision() { |
| return mrid.getRevision(); |
| } |
| } |
| |
| private final Comparator<ModuleRevisionId> comparator = new Comparator<ModuleRevisionId>() { |
| public int compare(ModuleRevisionId o1, ModuleRevisionId o2) { |
| if (o1.equals(o2)) { |
| return 0; |
| } |
| ArtifactInfo art1 = new MRIDArtifactInfo(o1); |
| ArtifactInfo art2 = new MRIDArtifactInfo(o2); |
| ArtifactInfo art = getLatestStrategy().findLatest(new ArtifactInfo[] {art1, art2}, |
| null); |
| return art == art1 ? -1 : 1; |
| } |
| }; |
| |
| private LatestStrategy latestStrategy; |
| |
| private String latestStrategyName = "default"; |
| |
| public VersionRangeMatcher() { |
| super("version-range"); |
| } |
| |
| public VersionRangeMatcher(String name) { |
| super(name); |
| } |
| |
| public VersionRangeMatcher(String name, LatestStrategy strategy) { |
| super(name); |
| this.latestStrategy = strategy; |
| } |
| |
| public boolean isDynamic(ModuleRevisionId askedMrid) { |
| String revision = askedMrid.getRevision(); |
| return ALL_RANGE.matcher(revision).matches(); |
| } |
| |
| public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) { |
| String revision = askedMrid.getRevision(); |
| Matcher m; |
| m = FINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| String lower = m.group(1); |
| String upper = m.group(2); |
| return isUpper(askedMrid, lower, foundMrid, revision.startsWith(OPEN_INC)) |
| && isLower(askedMrid, upper, foundMrid, revision.endsWith(CLOSE_INC)); |
| } |
| m = LOWER_INFINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| String upper = m.group(1); |
| return isLower(askedMrid, upper, foundMrid, revision.endsWith(CLOSE_INC)); |
| } |
| m = UPPER_INFINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| String lower = m.group(1); |
| return isUpper(askedMrid, lower, foundMrid, revision.startsWith(OPEN_INC)); |
| } |
| return false; |
| } |
| |
| private boolean isLower(ModuleRevisionId askedMrid, String revision, |
| ModuleRevisionId foundMrid, boolean inclusive) { |
| ModuleRevisionId mRevId = ModuleRevisionId.newInstance(askedMrid, revision); |
| int result = comparator.compare(mRevId, foundMrid); |
| return result <= (inclusive ? 0 : -1); |
| } |
| |
| private boolean isUpper(ModuleRevisionId askedMrid, String revision, |
| ModuleRevisionId foundMrid, boolean inclusive) { |
| ModuleRevisionId mRevId = ModuleRevisionId.newInstance(askedMrid, revision); |
| int result = comparator.compare(mRevId, foundMrid); |
| return result >= (inclusive ? 0 : 1); |
| } |
| |
| public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid, |
| Comparator<ModuleRevisionId> staticComparator) { |
| String revision = askedMrid.getRevision(); |
| Matcher m; |
| m = UPPER_INFINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| // no upper limit, the dynamic revision can always be considered greater |
| return 1; |
| } |
| String upper; |
| m = FINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| upper = m.group(2); |
| } else { |
| m = LOWER_INFINITE_RANGE.matcher(revision); |
| if (m.matches()) { |
| upper = m.group(1); |
| } else { |
| throw new IllegalArgumentException( |
| "impossible to compare: askedMrid is not a dynamic revision: " + askedMrid); |
| } |
| } |
| int c = staticComparator.compare(ModuleRevisionId.newInstance(askedMrid, upper), foundMrid); |
| // if the comparison consider them equal, we must return -1, because we can't consider the |
| // dynamic revision to be greater. Otherwise we can safely return the result of the static |
| // comparison |
| return c == 0 ? -1 : c; |
| } |
| |
| public LatestStrategy getLatestStrategy() { |
| if (latestStrategy == null) { |
| if (getSettings() == null) { |
| throw new IllegalStateException( |
| "no ivy instance nor latest strategy configured in version range matcher " |
| + this); |
| } |
| if (latestStrategyName == null) { |
| throw new IllegalStateException( |
| "null latest strategy defined in version range matcher " + this); |
| } |
| latestStrategy = getSettings().getLatestStrategy(latestStrategyName); |
| if (latestStrategy == null) { |
| throw new IllegalStateException("unknown latest strategy '" + latestStrategyName |
| + "' configured in version range matcher " + this); |
| } |
| } |
| return latestStrategy; |
| } |
| |
| public void setLatestStrategy(LatestStrategy latestStrategy) { |
| this.latestStrategy = latestStrategy; |
| } |
| |
| public void setLatest(String latestStrategyName) { |
| this.latestStrategyName = latestStrategyName; |
| } |
| |
| } |