001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.plugins.version;
019:
020: import java.util.Comparator;
021: import java.util.regex.Matcher;
022: import java.util.regex.Pattern;
023:
024: import org.apache.ivy.core.module.id.ModuleRevisionId;
025: import org.apache.ivy.plugins.latest.ArtifactInfo;
026: import org.apache.ivy.plugins.latest.LatestStrategy;
027:
028: /**
029: * Matches version ranges: [1.0,2.0] matches all versions greater or equal to 1.0 and lower or equal
030: * 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]
031: * matches all versions greater than 1.0 and lower or equal to 2.0 ]1.0,2.0[ matches all versions
032: * greater than 1.0 and lower than 2.0 [1.0,) matches all versions greater or equal to 1.0 ]1.0,)
033: * matches all versions greater than 1.0 (,2.0] matches all versions lower or equal to 2.0 (,2.0[
034: * matches all versions lower than 2.0 This class uses a latest strategy to compare revisions. If
035: * none is set, it uses the default one of the ivy instance set through setIvy(). If neither a
036: * latest strategy nor a ivy instance is set, an IllegalStateException will be thrown when calling
037: * accept(). Note that it can't work with latest time strategy, cause no time is known for the
038: * limits of the range. Therefore only purely revision based LatestStrategy can be used.
039: */
040: public class VersionRangeMatcher extends AbstractVersionMatcher {
041: // todo: check these constants
042: private static final String OPEN_INC = "[";
043:
044: private static final String OPEN_EXC = "]";
045:
046: private static final String CLOSE_INC = "]";
047:
048: private static final String CLOSE_EXC = "[";
049:
050: private static final String LOWER_INFINITE = "(";
051:
052: private static final String UPPER_INFINITE = ")";
053:
054: private static final String SEPARATOR = ",";
055:
056: // following patterns are built upon constants above and should not be modified
057: private static final String OPEN_INC_PATTERN = "\\" + OPEN_INC;
058:
059: private static final String OPEN_EXC_PATTERN = "\\" + OPEN_EXC;
060:
061: private static final String CLOSE_INC_PATTERN = "\\" + CLOSE_INC;
062:
063: private static final String CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC;
064:
065: private static final String LI_PATTERN = "\\" + LOWER_INFINITE;
066:
067: private static final String UI_PATTERN = "\\" + UPPER_INFINITE;
068:
069: private static final String SEP_PATTERN = "\\s*\\" + SEPARATOR
070: + "\\s*";
071:
072: private static final String OPEN_PATTERN = "[" + OPEN_INC_PATTERN
073: + OPEN_EXC_PATTERN + "]";
074:
075: private static final String CLOSE_PATTERN = "[" + CLOSE_INC_PATTERN
076: + CLOSE_EXC_PATTERN + "]";
077:
078: private static final String ANY_NON_SPECIAL_PATTERN = "[^\\s"
079: + SEPARATOR + OPEN_INC_PATTERN + OPEN_EXC_PATTERN
080: + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN
081: + UI_PATTERN + "]";
082:
083: private static final String FINITE_PATTERN = OPEN_PATTERN + "\\s*("
084: + ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + "("
085: + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
086:
087: private static final String LOWER_INFINITE_PATTERN = LI_PATTERN
088: + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*"
089: + CLOSE_PATTERN;
090:
091: private static final String UPPER_INFINITE_PATTERN = OPEN_PATTERN
092: + "\\s*(" + ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN
093: + UI_PATTERN;
094:
095: private static final Pattern FINITE_RANGE = Pattern
096: .compile(FINITE_PATTERN);
097:
098: private static final Pattern LOWER_INFINITE_RANGE = Pattern
099: .compile(LOWER_INFINITE_PATTERN);
100:
101: private static final Pattern UPPER_INFINITE_RANGE = Pattern
102: .compile(UPPER_INFINITE_PATTERN);
103:
104: private static final Pattern ALL_RANGE = Pattern
105: .compile(FINITE_PATTERN + "|" + LOWER_INFINITE_PATTERN
106: + "|" + UPPER_INFINITE_PATTERN);
107:
108: private final class MRIDArtifactInfo implements ArtifactInfo {
109: private ModuleRevisionId mrid;
110:
111: public MRIDArtifactInfo(ModuleRevisionId id) {
112: mrid = id;
113: }
114:
115: public long getLastModified() {
116: return 0;
117: }
118:
119: public String getRevision() {
120: return mrid.getRevision();
121: }
122: }
123:
124: private final Comparator comparator = new Comparator() {
125: public int compare(Object o1, Object o2) {
126: if (o1.equals(o2)) {
127: return 0;
128: }
129: ArtifactInfo art1 = new MRIDArtifactInfo(
130: (ModuleRevisionId) o1);
131: ArtifactInfo art2 = new MRIDArtifactInfo(
132: (ModuleRevisionId) o2);
133: ArtifactInfo art = getLatestStrategy().findLatest(
134: new ArtifactInfo[] { art1, art2 }, null);
135: return art == art1 ? -1 : 1;
136: }
137: };
138:
139: private LatestStrategy latestStrategy;
140:
141: private String latestStrategyName = "default";
142:
143: public VersionRangeMatcher() {
144: super ("version-range");
145: }
146:
147: public VersionRangeMatcher(String name) {
148: super (name);
149: }
150:
151: public VersionRangeMatcher(String name, LatestStrategy strategy) {
152: super (name);
153: this .latestStrategy = strategy;
154: }
155:
156: public boolean isDynamic(ModuleRevisionId askedMrid) {
157: String revision = askedMrid.getRevision();
158: return ALL_RANGE.matcher(revision).matches();
159: }
160:
161: public boolean accept(ModuleRevisionId askedMrid,
162: ModuleRevisionId foundMrid) {
163: String revision = askedMrid.getRevision();
164: Matcher m;
165: m = FINITE_RANGE.matcher(revision);
166: if (m.matches()) {
167: String lower = m.group(1);
168: String upper = m.group(2);
169: return isUpper(askedMrid, lower, foundMrid, revision
170: .startsWith(OPEN_INC))
171: && isLower(askedMrid, upper, foundMrid, revision
172: .endsWith(CLOSE_INC));
173: }
174: m = LOWER_INFINITE_RANGE.matcher(revision);
175: if (m.matches()) {
176: String upper = m.group(1);
177: return isLower(askedMrid, upper, foundMrid, revision
178: .endsWith(CLOSE_INC));
179: }
180: m = UPPER_INFINITE_RANGE.matcher(revision);
181: if (m.matches()) {
182: String lower = m.group(1);
183: return isUpper(askedMrid, lower, foundMrid, revision
184: .startsWith(OPEN_INC));
185: }
186: return false;
187: }
188:
189: private boolean isLower(ModuleRevisionId askedMrid,
190: String revision, ModuleRevisionId foundMrid,
191: boolean inclusive) {
192: ModuleRevisionId mRevId = ModuleRevisionId.newInstance(
193: askedMrid, revision);
194: int result = comparator.compare(mRevId, foundMrid);
195: return result <= (inclusive ? 0 : -1);
196: }
197:
198: private boolean isUpper(ModuleRevisionId askedMrid,
199: String revision, ModuleRevisionId foundMrid,
200: boolean inclusive) {
201: ModuleRevisionId mRevId = ModuleRevisionId.newInstance(
202: askedMrid, revision);
203: int result = comparator.compare(mRevId, foundMrid);
204: return result >= (inclusive ? 0 : 1);
205: }
206:
207: public int compare(ModuleRevisionId askedMrid,
208: ModuleRevisionId foundMrid, Comparator staticComparator) {
209: String revision = askedMrid.getRevision();
210: Matcher m;
211: m = UPPER_INFINITE_RANGE.matcher(revision);
212: if (m.matches()) {
213: // no upper limit, the dynamic revision can always be considered greater
214: return 1;
215: }
216: String upper;
217: m = FINITE_RANGE.matcher(revision);
218: if (m.matches()) {
219: upper = m.group(2);
220: } else {
221: m = LOWER_INFINITE_RANGE.matcher(revision);
222: if (m.matches()) {
223: upper = m.group(1);
224: } else {
225: throw new IllegalArgumentException(
226: "impossible to compare: askedMrid is not a dynamic revision: "
227: + askedMrid);
228: }
229: }
230: int c = staticComparator.compare(ModuleRevisionId.newInstance(
231: askedMrid, upper), foundMrid);
232: // if the comparison consider them equal, we must return -1, because we can't consider the
233: // dynamic revision to be greater. Otherwise we can safeely return the result of the static
234: // comparison
235: return c == 0 ? -1 : c;
236: }
237:
238: public LatestStrategy getLatestStrategy() {
239: if (latestStrategy == null) {
240: if (getSettings() == null) {
241: throw new IllegalStateException(
242: "no ivy instance nor latest strategy configured in version range matcher "
243: + this );
244: }
245: if (latestStrategyName == null) {
246: throw new IllegalStateException(
247: "null latest strategy defined in version range matcher "
248: + this );
249: }
250: latestStrategy = getSettings().getLatestStrategy(
251: latestStrategyName);
252: if (latestStrategy == null) {
253: throw new IllegalStateException(
254: "unknown latest strategy '"
255: + latestStrategyName
256: + "' configured in version range matcher "
257: + this );
258: }
259: }
260: return latestStrategy;
261: }
262:
263: public void setLatestStrategy(LatestStrategy latestStrategy) {
264: this .latestStrategy = latestStrategy;
265: }
266:
267: public void setLatest(String latestStrategyName) {
268: this.latestStrategyName = latestStrategyName;
269: }
270:
271: }
|