com.metaeffekt.artifact.analysis.version.curation.ConditionalCuratedVersionPartsExtractor Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2021-2024 the original author or authors.
*
* 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 com.metaeffekt.artifact.analysis.version.curation;
import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.version.curation.functions.CuratedVersionFunction;
import com.metaeffekt.artifact.analysis.version.token.VersionToken;
import com.metaeffekt.artifact.analysis.version.token.VersionTokenType;
import com.metaeffekt.artifact.analysis.version.token.VersionTokenizer;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class ConditionalCuratedVersionPartsExtractor {
private final static Logger LOG = LoggerFactory.getLogger(ConditionalCuratedVersionPartsExtractor.class);
private final VersionContextMatcher matcher;
private final Pattern pattern;
private final String specVersion;
private final String semVersion;
private final String buildVersion;
private final String versionModifier;
private final String otherVersionPart;
private final String afterAllPart;
private final String preprocessingPattern;
private final List postProcessingFunctions;
public ConditionalCuratedVersionPartsExtractor(VersionContextMatcher matcher, String pattern, int patternFlags, String specVersion, String semVersion, String buildVersion, String versionModifier, String otherVersionPart, String afterAllPart, String preprocessingPattern, List postProcessingFunctions) {
this.matcher = matcher;
this.pattern = pattern == null ? null : Pattern.compile(pattern, patternFlags);
this.specVersion = specVersion;
this.semVersion = semVersion;
this.buildVersion = buildVersion;
this.versionModifier = versionModifier;
this.otherVersionPart = otherVersionPart;
this.afterAllPart = afterAllPart;
this.preprocessingPattern = preprocessingPattern;
this.postProcessingFunctions = postProcessingFunctions;
}
private static int parsePatternFlags(String patternFlagsString) {
int patternFlags = 0;
if (patternFlagsString != null) {
for (char c : patternFlagsString.toCharArray()) {
switch (c) {
case 'i':
patternFlags |= Pattern.CASE_INSENSITIVE;
break;
case 'm':
patternFlags |= Pattern.MULTILINE;
break;
case 's':
patternFlags |= Pattern.DOTALL;
break;
case 'u':
patternFlags |= Pattern.UNICODE_CASE;
break;
case 'x':
patternFlags |= Pattern.COMMENTS;
break;
default:
throw new IllegalArgumentException("Unknown pattern flag: " + c);
}
}
}
return patternFlags;
}
public ExtractedCuratedVersionParts applyFirstStep(String version, VersionContext context) {
if (this.matcher != null && !this.matcher.matches(context)) {
return null;
}
final Matcher matcher = matcherForExtractorPattern(version);
if (matcher != null && matcher.matches()) {
if (preprocessingPattern != null) {
return new ExtractedCuratedVersionParts(applyPreprocessingPattern(matcher));
} else {
return new ExtractedCuratedVersionParts(
applySpecVersion(matcher),
applySemVersion(matcher),
applyBuildVersion(matcher),
applyVersionModifier(matcher),
applyOtherVersionPart(matcher),
applyAfterAllPart(matcher));
}
}
return null;
}
public void applySecondStep(ExtractedCuratedVersionParts parts) {
if (parts == null) {
return;
}
for (CuratedVersionFunction function : postProcessingFunctions) {
function.apply(parts);
}
}
public boolean matches(String version, VersionContext context) {
if (this.matcher != null && !this.matcher.matches(context)) {
return false;
}
if (this.pattern == null) {
return true;
}
final Matcher matcher = matcherForExtractorPattern(version);
return matcher != null && matcher.matches();
}
private Matcher matcherForExtractorPattern(String version) {
if (version == null || this.pattern == null) return null;
return this.pattern.matcher(version);
}
private VersionToken applySpecVersion(Matcher matcher) {
return applyTokenization(applyPattern(matcher, specVersion));
}
private VersionToken applySemVersion(Matcher matcher) {
return applyTokenization(applyPattern(matcher, semVersion));
}
private VersionToken applyBuildVersion(Matcher matcher) {
return applyTokenization(applyPattern(matcher, buildVersion));
}
private VersionToken applyVersionModifier(Matcher matcher) {
return applyTokenization(applyPattern(matcher, versionModifier));
}
private VersionToken applyOtherVersionPart(Matcher matcher) {
return applyTokenization(applyPattern(matcher, otherVersionPart));
}
private VersionToken applyAfterAllPart(Matcher matcher) {
return applyTokenization(applyPattern(matcher, afterAllPart));
}
private String applyPreprocessingPattern(Matcher matcher) {
return applyPattern(matcher, preprocessingPattern);
}
private VersionToken applyTokenization(String str) {
final List tokenizedVersion = VersionTokenizer.tokenize(str, null);
if (tokenizedVersion.size() == 1) {
return tokenizedVersion.get(0);
} else if (!tokenizedVersion.isEmpty()) {
return new VersionToken(str, VersionTokenType.STRING);
}
return null;
}
private String applyPattern(Matcher matcher, String replacement) {
if (replacement == null) return null;
return matcher.replaceAll(replacement);
}
public JSONObject toJson() {
return new JSONObject()
.put("pattern", this.pattern == null ? null : this.pattern.pattern())
.put("pattern-flags", this.pattern == null ? null : this.pattern.flags())
.put("segments", new JSONObject()
.put("spec", specVersion)
.put("sem", semVersion)
.put("build", buildVersion)
.put("modifier", versionModifier)
.put("other", otherVersionPart)
.put("after-all", afterAllPart)
.put("full", preprocessingPattern))
.put("context", matcher == null ? null : matcher.toJson())
.put("second-part-functions", postProcessingFunctions.stream().map(CuratedVersionFunction::toJson).collect(Collectors.toList()));
}
public static ConditionalCuratedVersionPartsExtractor fromYaml(Map yaml) {
final String pattern = (String) yaml.get("pattern");
final String patternFlagsString = (String) yaml.get("pattern-flags");
// extract every char that is a valid flag and create a flag int
int patternFlags = parsePatternFlags(patternFlagsString);
final Map segments = (Map) yaml.get("segments");
final String specVersion;
final String semVersion;
final String buildVersion;
final String versionModifier;
final String otherVersionPart;
final String afterAllPart;
if (segments != null) {
specVersion = (String) segments.get("spec");
semVersion = (String) segments.get("sem");
buildVersion = (String) segments.get("build");
versionModifier = (String) segments.get("modifier");
otherVersionPart = (String) segments.get("other");
afterAllPart = (String) segments.get("after-all");
} else {
specVersion = null;
semVersion = null;
buildVersion = null;
versionModifier = null;
otherVersionPart = null;
afterAllPart = null;
}
final String preprocessorPart = (String) yaml.get("preprocessor");
final VersionContextMatcher matcher;
if (yaml.containsKey("context")) {
if (yaml.get("context") instanceof LinkedHashMap) {
matcher = VersionContextMatcher.fromYaml((Map) yaml.get("context"));
} else {
LOG.warn("Invalid context definition in extractor: {}", yaml.get("context"));
matcher = null;
}
} else {
matcher = null;
}
final String effectivePattern = StringUtils.isEmpty(pattern) ? null
: pattern
.replace("__YEAR__", __YEAR__.pattern())
.replace("__SEMVER__", __SEMVER__.pattern())
.replace("__LETTER__", __LETTER__.pattern())
.replace("__SEPARATOR__", __SEPARATOR__.pattern())
.replace("__NUMBER__", __NUMBER__.pattern());
final List postProcessingFunctions = new ArrayList<>();
if (yaml.containsKey("functions") && yaml.get("functions") instanceof List) {
for (Object function : (List) yaml.get("functions")) {
if (function instanceof LinkedHashMap) {
postProcessingFunctions.add(CuratedVersionFunction.fromYaml((LinkedHashMap) function));
}
}
}
postProcessingFunctions.removeIf(Objects::isNull);
return new ConditionalCuratedVersionPartsExtractor(matcher, effectivePattern, patternFlags,
specVersion, semVersion, buildVersion, versionModifier, otherVersionPart, afterAllPart,
preprocessorPart,
postProcessingFunctions
);
}
private final static Pattern __YEAR__ = Pattern.compile("[0-9]{4}");
private final static Pattern __SEMVER__ = Pattern.compile("[0-9]+\\.[0-9]+(?:\\.[0-9]+)*?");
private final static Pattern __NUMBER__ = Pattern.compile("[0-9]+");
private final static Pattern __LETTER__ = Pattern.compile("[a-zA-Z]+");
private final static Pattern __SEPARATOR__ = Pattern.compile("[-_ :]");
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy