| /* |
| * 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.ant; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.regex.Pattern; |
| |
| import org.apache.ivy.Ivy; |
| import org.apache.ivy.core.module.id.ModuleId; |
| import org.apache.ivy.core.module.id.ModuleRevisionId; |
| import org.apache.ivy.core.resolve.ResolvedModuleRevision; |
| import org.apache.ivy.core.search.SearchEngine; |
| import org.apache.ivy.core.settings.IvySettings; |
| import org.apache.ivy.plugins.latest.ArtifactInfo; |
| import org.apache.ivy.plugins.latest.LatestStrategy; |
| import org.apache.ivy.plugins.matcher.ExactOrRegexpPatternMatcher; |
| import org.apache.ivy.plugins.matcher.ExactPatternMatcher; |
| import org.apache.ivy.plugins.matcher.Matcher; |
| import org.apache.ivy.plugins.matcher.PatternMatcher; |
| import org.apache.ivy.plugins.resolver.DependencyResolver; |
| import org.apache.ivy.plugins.version.VersionMatcher; |
| import org.apache.tools.ant.BuildException; |
| |
| import static org.apache.ivy.util.StringUtils.isNullOrEmpty; |
| |
| /** |
| * Look for the latest module in the repository matching the given criteria, and sets a set of |
| * properties according to what was found. |
| */ |
| public class IvyBuildNumber extends IvyTask { |
| |
| public static class ResolvedModuleRevisionArtifactInfo implements ArtifactInfo { |
| private ModuleRevisionId rmr; |
| |
| public ResolvedModuleRevisionArtifactInfo(ModuleRevisionId rmr) { |
| this.rmr = rmr; |
| } |
| |
| public String getRevision() { |
| return rmr.getRevision(); |
| } |
| |
| public long getLastModified() { |
| return -1; |
| } |
| |
| } |
| |
| private String organisation; |
| |
| private String module; |
| |
| private String branch; |
| |
| private String revision; |
| |
| private String revSep = "."; |
| |
| private String prefix = "ivy."; |
| |
| private String defaultValue = "0"; |
| |
| private String defaultBuildNumber = "0"; |
| |
| private String resolver = null; |
| |
| public String getModule() { |
| return module; |
| } |
| |
| public void setModule(String module) { |
| this.module = module; |
| } |
| |
| public String getOrganisation() { |
| return organisation; |
| } |
| |
| public void setOrganisation(String organisation) { |
| this.organisation = organisation; |
| } |
| |
| public String getRevision() { |
| return revision; |
| } |
| |
| public void setRevision(String revision) { |
| this.revision = revision; |
| } |
| |
| public String getBranch() { |
| return branch; |
| } |
| |
| public void setBranch(String branch) { |
| this.branch = branch; |
| } |
| |
| public String getDefault() { |
| return defaultValue; |
| } |
| |
| public void setDefault(String default1) { |
| defaultValue = default1; |
| } |
| |
| public String getResolver() { |
| return resolver; |
| } |
| |
| public void setResolver(String resolver) { |
| this.resolver = resolver; |
| } |
| |
| public String getPrefix() { |
| return prefix; |
| } |
| |
| public void setPrefix(String prefix) { |
| this.prefix = prefix; |
| } |
| |
| public void doExecute() throws BuildException { |
| if (organisation == null) { |
| throw new BuildException("no organisation provided for ivy buildnumber task"); |
| } |
| if (module == null) { |
| throw new BuildException("no module name provided for ivy buildnumber task"); |
| } |
| if (prefix == null) { |
| throw new BuildException("null prefix not allowed"); |
| } |
| |
| Ivy ivy = getIvyInstance(); |
| IvySettings settings = ivy.getSettings(); |
| if (branch == null) { |
| branch = settings.getDefaultBranch(new ModuleId(organisation, module)); |
| } |
| if (isNullOrEmpty(revision)) { |
| revision = "latest.integration"; |
| } else if (!revision.endsWith("+")) { |
| revision += "+"; |
| } |
| if (!prefix.endsWith(".") && !prefix.isEmpty()) { |
| prefix += "."; |
| } |
| |
| SearchEngine searcher = new SearchEngine(settings); |
| |
| PatternMatcher patternMatcher = new PatternMatcher() { |
| private PatternMatcher exact = new ExactPatternMatcher(); |
| |
| private PatternMatcher regexp = new ExactOrRegexpPatternMatcher(); |
| |
| public Matcher getMatcher(String expression) { |
| if (expression.equals(organisation) || expression.equals(module) |
| || expression.equals(branch)) { |
| return exact.getMatcher(expression); |
| } else { |
| return regexp.getMatcher(expression); |
| } |
| } |
| |
| public String getName() { |
| return "buildnumber-matcher"; |
| } |
| }; |
| |
| String revisionPattern = ".*"; |
| if (revision.endsWith("+")) { |
| revisionPattern = Pattern.quote(revision.substring(0, revision.length() - 1)) + ".*"; |
| } |
| |
| ModuleRevisionId mrid = ModuleRevisionId.newInstance(organisation, module, branch, |
| revisionPattern); |
| ModuleRevisionId[] revisions; |
| if (resolver == null) { |
| revisions = searcher.listModules(mrid, patternMatcher); |
| } else { |
| DependencyResolver depResolver = settings.getResolver(resolver); |
| if (depResolver == null) { |
| throw new BuildException("Unknown resolver: " + resolver); |
| } |
| revisions = searcher.listModules(depResolver, mrid, patternMatcher); |
| } |
| |
| List<ArtifactInfo> infos = new ArrayList<>(revisions.length); |
| for (ModuleRevisionId rev : revisions) { |
| infos.add(new ResolvedModuleRevisionArtifactInfo(rev)); |
| } |
| |
| VersionMatcher matcher = settings.getVersionMatcher(); |
| LatestStrategy latestStrategy = settings.getLatestStrategy("latest-revision"); |
| List<ArtifactInfo> sorted = latestStrategy.sort(infos.toArray(new ArtifactInfo[revisions.length])); |
| |
| ModuleRevisionId askedMrid = ModuleRevisionId.newInstance(organisation, module, branch, |
| revision); |
| |
| String foundRevision = null; |
| ListIterator<ArtifactInfo> iter = sorted.listIterator(sorted.size()); |
| while (iter.hasPrevious()) { |
| ResolvedModuleRevisionArtifactInfo info = (ResolvedModuleRevisionArtifactInfo) iter |
| .previous(); |
| |
| if (!matcher.accept(askedMrid, info.rmr)) { |
| continue; |
| } |
| |
| if (matcher.needModuleDescriptor(askedMrid, info.rmr)) { |
| ResolvedModuleRevision rmr = ivy.findModule(info.rmr); |
| if (matcher.accept(askedMrid, rmr.getDescriptor())) { |
| foundRevision = info.rmr.getRevision(); |
| } |
| } else { |
| foundRevision = info.rmr.getRevision(); |
| } |
| |
| if (foundRevision != null) { |
| break; |
| } |
| } |
| |
| NewRevision newRevision = computeNewRevision(foundRevision); |
| setProperty("revision", newRevision.getRevision()); |
| setProperty("new.revision", newRevision.getNewRevision()); |
| setProperty("build.number", newRevision.getBuildNumber()); |
| setProperty("new.build.number", newRevision.getNewBuildNumber()); |
| } |
| |
| private void setProperty(String propertyName, String value) { |
| if (value != null) { |
| getProject().setProperty(prefix + propertyName, value); |
| } |
| } |
| |
| private NewRevision computeNewRevision(String revision) { |
| String revPrefix = "latest.integration".equals(this.revision) ? "" : this.revision |
| .substring(0, this.revision.length() - 1); |
| if (revision != null && !revision.startsWith(revPrefix)) { |
| throw new BuildException("invalid exception found in repository: '" + revision |
| + "' for '" + revPrefix + "'"); |
| } |
| if (revision == null) { |
| if (revPrefix.length() > 0) { |
| return new NewRevision(revision, revPrefix |
| + (revPrefix.endsWith(revSep) ? defaultBuildNumber : revSep |
| + defaultBuildNumber), null, defaultBuildNumber); |
| } else { |
| Range r = findLastNumber(defaultValue); |
| if (r == null) { // no number found |
| return new NewRevision(revision, defaultValue, null, null); |
| } else { |
| long n = Long.parseLong(defaultValue.substring(r.getStartIndex(), |
| r.getEndIndex())); |
| return new NewRevision(revision, defaultValue, null, String.valueOf(n)); |
| } |
| } |
| } |
| Range r; |
| if (revPrefix.length() == 0) { |
| r = findLastNumber(revision); |
| if (r == null) { |
| return new NewRevision(revision, revision |
| + (revision.endsWith(revSep) ? "1" : revSep + "1"), null, "1"); |
| } |
| } else { |
| r = findFirstNumber(revision, revPrefix.length()); |
| if (r == null) { |
| return new NewRevision(revision, revPrefix |
| + (revPrefix.endsWith(revSep) ? "1" : revSep + "1"), null, "1"); |
| } |
| } |
| long n = Long.parseLong(revision.substring(r.getStartIndex(), r.getEndIndex())) + 1; |
| return new NewRevision(revision, revision.substring(0, r.getStartIndex()) + n, |
| String.valueOf(n - 1), String.valueOf(n)); |
| } |
| |
| private Range findFirstNumber(String str, int startIndex) { |
| // let's find the first digit in the string |
| int startNumberIndex = startIndex; |
| while (startNumberIndex < str.length() && !Character.isDigit(str.charAt(startNumberIndex))) { |
| startNumberIndex++; |
| } |
| if (startNumberIndex == str.length()) { |
| return null; |
| } |
| // let's find the end of the number |
| int endNumberIndex = startNumberIndex + 1; |
| while (endNumberIndex < str.length() && Character.isDigit(str.charAt(endNumberIndex))) { |
| endNumberIndex++; |
| } |
| return new Range(startNumberIndex, endNumberIndex); |
| } |
| |
| private Range findLastNumber(String str) { |
| int endNumberIndex = str.length() - 1; |
| while (endNumberIndex >= 0 && !Character.isDigit(str.charAt(endNumberIndex))) { |
| endNumberIndex--; |
| } |
| int startNumberIndex = (endNumberIndex == -1) ? -1 : endNumberIndex - 1; |
| while (startNumberIndex >= 0 && Character.isDigit(str.charAt(startNumberIndex))) { |
| startNumberIndex--; |
| } |
| endNumberIndex++; |
| startNumberIndex++; |
| if (startNumberIndex == endNumberIndex) { // no number found |
| return null; |
| } else { |
| return new Range(startNumberIndex, endNumberIndex); |
| } |
| } |
| |
| private static class Range { |
| private int startIndex; |
| |
| private int endIndex; |
| |
| public Range(int startIndex, int endIndex) { |
| this.startIndex = startIndex; |
| this.endIndex = endIndex; |
| } |
| |
| public int getStartIndex() { |
| return startIndex; |
| } |
| |
| public int getEndIndex() { |
| return endIndex; |
| } |
| } |
| |
| private static class NewRevision { |
| private String revision; |
| |
| private String newRevision; |
| |
| private String buildNumber; |
| |
| private String newBuildNumber; |
| |
| public NewRevision(String revision, String newRevision, String buildNumber, |
| String newBuildNumber) { |
| this.revision = revision; |
| this.newRevision = newRevision; |
| this.buildNumber = buildNumber; |
| this.newBuildNumber = newBuildNumber; |
| } |
| |
| public String getRevision() { |
| return revision; |
| } |
| |
| public String getNewRevision() { |
| return newRevision; |
| } |
| |
| public String getBuildNumber() { |
| return buildNumber; |
| } |
| |
| public String getNewBuildNumber() { |
| return newBuildNumber; |
| } |
| } |
| |
| public String getRevSep() { |
| return revSep; |
| } |
| |
| public void setRevSep(String revSep) { |
| this.revSep = revSep; |
| } |
| |
| public String getDefaultBuildNumber() { |
| return defaultBuildNumber; |
| } |
| |
| public void setDefaultBuildNumber(String defaultBuildNumber) { |
| this.defaultBuildNumber = defaultBuildNumber; |
| } |
| } |