blob: 3dacb7d54b28817519551370f4b03e812a356650 [file] [log] [blame]
/*
* 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.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ivy.core.cache.ArtifactOrigin;
import org.apache.ivy.core.cache.RepositoryCacheManager;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.settings.IvyVariableContainer;
import org.apache.ivy.core.settings.IvyVariableContainerImpl;
import org.apache.ivy.util.Message;
import static org.apache.ivy.util.StringUtils.isNullOrEmpty;
/**
*/
public final class IvyPatternHelper {
private IvyPatternHelper() {
// Helper class
}
public static final String CONF_KEY = "conf";
public static final String TYPE_KEY = "type";
public static final String EXT_KEY = "ext";
public static final String ARTIFACT_KEY = "artifact";
public static final String BRANCH_KEY = "branch";
public static final String REVISION_KEY = "revision";
public static final String MODULE_KEY = "module";
public static final String ORGANISATION_KEY = "organisation";
public static final String ORGANISATION_KEY2 = "organization";
public static final String ORGANISATION_PATH_KEY = "orgPath";
public static final String ORIGINAL_ARTIFACTNAME_KEY = "originalname";
private static final Pattern PARAM_PATTERN = Pattern.compile("\\@\\{(.*?)\\}");
private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)\\}");
public static String substitute(String pattern, ModuleRevisionId moduleRevision) {
return substitute(pattern, moduleRevision.getOrganisation(), moduleRevision.getName(),
moduleRevision.getBranch(), moduleRevision.getRevision(), "ivy", "ivy", "xml", null,
null, moduleRevision.getQualifiedExtraAttributes(), null);
}
public static String substitute(String pattern, ModuleRevisionId moduleRevision,
String artifact, String type, String ext) {
return substitute(pattern, moduleRevision, new DefaultArtifact(moduleRevision, null,
artifact, type, ext));
}
public static String substitute(String pattern, Artifact artifact) {
return substitute(pattern, artifact, (String) null);
}
public static String substitute(String pattern, Artifact artifact, ArtifactOrigin origin) {
return substitute(pattern, artifact.getModuleRevisionId(), artifact, null, origin);
}
public static String substitute(String pattern, Artifact artifact, String conf) {
return substitute(pattern, artifact.getModuleRevisionId(), artifact, conf, null);
}
public static String substitute(String pattern, ModuleRevisionId mrid, Artifact artifact) {
return substitute(pattern, mrid, artifact, null, null);
}
public static String substitute(String pattern, ModuleRevisionId mrid, Artifact artifact,
String conf, ArtifactOrigin origin) {
return substitute(pattern, mrid.getOrganisation(), mrid.getName(), mrid.getBranch(),
mrid.getRevision(), artifact.getName(), artifact.getType(), artifact.getExt(), conf,
origin, mrid.getQualifiedExtraAttributes(), artifact.getQualifiedExtraAttributes());
}
public static String substitute(String pattern, String org, String module, String revision,
String artifact, String type, String ext) {
return substitute(pattern, org, module, null, revision, artifact, type, ext,
null, null, null, null);
}
// CheckStyle:ParameterNumber OFF
public static String substitute(String pattern, String org, String module, String revision,
String artifact, String type, String ext, String conf) {
return substitute(pattern, org, module, null, revision, artifact, type, ext, conf,
null, null, null);
}
public static String substitute(String pattern, String org, String module, String revision,
String artifact, String type, String ext, String conf, Map<String, String> extraModuleAttributes,
Map<String, String> extraArtifactAttributes) {
return substitute(pattern, org, module, null, revision, artifact, type, ext, conf,
null, extraModuleAttributes, extraArtifactAttributes);
}
public static String substitute(String pattern, String org, String module, String branch,
String revision, String artifact, String type, String ext, String conf,
ArtifactOrigin origin, Map<String, String> extraModuleAttributes, Map<String, String> extraArtifactAttributes) {
Map<String, Object> tokens = new HashMap<>();
if (extraModuleAttributes != null) {
for (Map.Entry<String, String> entry : extraModuleAttributes.entrySet()) {
String token = entry.getKey();
if (token.indexOf(':') > 0) {
token = token.substring(token.indexOf(':') + 1);
}
tokens.put(token, entry.getValue());
}
}
if (extraArtifactAttributes != null) {
for (Map.Entry<String, String> entry : extraArtifactAttributes.entrySet()) {
String token = entry.getKey();
if (token.indexOf(':') > 0) {
token = token.substring(token.indexOf(':') + 1);
}
tokens.put(token, entry.getValue());
}
}
tokens.put(ORGANISATION_KEY, org == null ? "" : org);
tokens.put(ORGANISATION_KEY2, org == null ? "" : org);
tokens.put(ORGANISATION_PATH_KEY, org == null ? "" : org.replace('.', '/'));
tokens.put(MODULE_KEY, module == null ? "" : module);
tokens.put(BRANCH_KEY, branch == null ? "" : branch);
tokens.put(REVISION_KEY, revision == null ? "" : revision);
tokens.put(ARTIFACT_KEY, artifact == null ? module : artifact);
tokens.put(TYPE_KEY, type == null ? "jar" : type);
tokens.put(EXT_KEY, ext == null ? "jar" : ext);
tokens.put(CONF_KEY, conf == null ? "default" : conf);
if (origin == null) {
tokens.put(ORIGINAL_ARTIFACTNAME_KEY, new OriginalArtifactNameValue(org, module,
branch, revision, artifact, type, ext, extraModuleAttributes,
extraArtifactAttributes));
} else {
tokens.put(ORIGINAL_ARTIFACTNAME_KEY, new OriginalArtifactNameValue(origin));
}
return substituteTokens(pattern, tokens, false);
}
// CheckStyle:ParameterNumber ON
public static String substituteVariables(String pattern, Map<String, String> variables) {
return substituteVariables(pattern, new IvyVariableContainerImpl(variables), new Stack<String>());
}
public static String substituteVariables(String pattern, IvyVariableContainer variables) {
return substituteVariables(pattern, variables, new Stack<String>());
}
private static String substituteVariables(String pattern, IvyVariableContainer variables,
Stack<String> substituting) {
// if you supply null, null is what you get
if (pattern == null) {
return null;
}
Matcher m = VAR_PATTERN.matcher(pattern);
boolean useVariables = false;
StringBuffer sb = null;
while (m.find()) {
if (!useVariables) {
useVariables = true;
sb = new StringBuffer();
}
String var = m.group(1);
String val = variables.getVariable(var);
if (val != null) {
int index = substituting.indexOf(var);
if (index != -1) {
List<String> cycle = new ArrayList<>(substituting.subList(index, substituting.size()));
cycle.add(var);
throw new IllegalArgumentException("cyclic variable definition: cycle = "
+ cycle);
}
substituting.push(var);
val = substituteVariables(val, variables, substituting);
substituting.pop();
} else {
val = m.group();
}
m.appendReplacement(sb, val.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$", "\\\\\\$"));
}
if (useVariables) {
m.appendTail(sb);
return sb.toString();
} else {
return pattern;
}
}
// This is a cludge to reconcile different values passed to the method
public static String substituteTokens(String pattern, Map<String, String> tokens) {
Map<String, Object> tokensCopy = new HashMap<>();
tokensCopy.putAll(tokens);
return substituteTokens(pattern, tokensCopy, true);
}
private static String substituteTokens(String pattern, Map<String, Object> tokens, boolean external) {
Map<String, Object> tokensCopy = external ? tokens : new HashMap<>(tokens);
if (tokensCopy.containsKey(ORGANISATION_KEY) && !tokensCopy.containsKey(ORGANISATION_KEY2)) {
tokensCopy.put(ORGANISATION_KEY2, tokensCopy.get(ORGANISATION_KEY));
}
if (tokensCopy.containsKey(ORGANISATION_KEY)
&& !tokensCopy.containsKey(ORGANISATION_PATH_KEY)) {
String org = (String) tokensCopy.get(ORGANISATION_KEY);
tokensCopy.put(ORGANISATION_PATH_KEY, org == null ? "" : org.replace('.', '/'));
}
StringBuilder buffer = new StringBuilder();
StringBuffer optionalPart = null;
StringBuffer tokenBuffer = null;
boolean insideOptionalPart = false;
boolean insideToken = false;
boolean tokenSeen = false;
boolean tokenHadValue = false;
for (char ch : pattern.toCharArray()) {
int i = pattern.indexOf(ch);
switch (ch) {
case '(':
if (insideOptionalPart) {
throw new IllegalArgumentException(
"invalid start of optional part at position " + i + " in pattern "
+ pattern);
}
optionalPart = new StringBuffer();
insideOptionalPart = true;
tokenSeen = false;
tokenHadValue = false;
break;
case ')':
if (!insideOptionalPart || insideToken) {
throw new IllegalArgumentException(
"invalid end of optional part at position " + i + " in pattern "
+ pattern);
}
if (tokenHadValue) {
buffer.append(optionalPart.toString());
} else if (!tokenSeen) {
buffer.append('(').append(optionalPart.toString()).append(')');
}
insideOptionalPart = false;
break;
case '[':
if (insideToken) {
throw new IllegalArgumentException("invalid start of token at position "
+ i + " in pattern " + pattern);
}
tokenBuffer = new StringBuffer();
insideToken = true;
break;
case ']':
if (!insideToken) {
throw new IllegalArgumentException("invalid end of token at position " + i
+ " in pattern " + pattern);
}
String token = tokenBuffer.toString();
Object tokenValue = tokensCopy.get(token);
String value = (tokenValue == null) ? null : tokenValue.toString();
if (insideOptionalPart) {
tokenHadValue = !isNullOrEmpty(value);
optionalPart.append(value);
} else {
if (value == null) { // the token wasn't set, it's kept as is
value = "[" + token + "]";
}
buffer.append(value);
}
insideToken = false;
tokenSeen = true;
break;
default:
if (insideToken) {
tokenBuffer.append(ch);
} else if (insideOptionalPart) {
optionalPart.append(ch);
} else {
buffer.append(ch);
}
break;
}
}
if (insideToken) {
throw new IllegalArgumentException("last token hasn't been closed in pattern "
+ pattern);
}
if (insideOptionalPart) {
throw new IllegalArgumentException("optional part hasn't been closed in pattern "
+ pattern);
}
return buffer.toString();
}
public static String substituteVariable(String pattern, String variable, String value) {
StringBuffer buf = new StringBuffer(pattern);
substituteVariable(buf, variable, value);
return buf.toString();
}
public static void substituteVariable(StringBuffer buf, String variable, String value) {
String from = "${" + variable + "}";
int fromLength = from.length();
for (int index = buf.indexOf(from); index != -1; index = buf.indexOf(from, index)) {
buf.replace(index, index + fromLength, value);
}
}
public static String substituteToken(String pattern, String token, String value) {
StringBuffer buf = new StringBuffer(pattern);
substituteToken(buf, token, value);
return buf.toString();
}
public static void substituteToken(StringBuffer buf, String token, String value) {
String from = getTokenString(token);
int fromLength = from.length();
for (int index = buf.indexOf(from); index != -1; index = buf.indexOf(from, index)) {
buf.replace(index, index + fromLength, value);
}
}
public static String getTokenString(String token) {
return "[" + token + "]";
}
public static String substituteParams(String pattern, Map<String, String> params) {
return substituteParams(pattern, new IvyVariableContainerImpl(params), new Stack<String>());
}
private static String substituteParams(String pattern, IvyVariableContainer params,
Stack<String> substituting) {
// TODO : refactor this with substituteVariables
// if you supply null, null is what you get
if (pattern == null) {
return null;
}
Matcher m = PARAM_PATTERN.matcher(pattern);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String var = m.group(1);
String val = params.getVariable(var);
if (val != null) {
int index = substituting.indexOf(var);
if (index != -1) {
List<String> cycle = new ArrayList<>(substituting.subList(index, substituting.size()));
cycle.add(var);
throw new IllegalArgumentException("cyclic param definition: cycle = " + cycle);
}
substituting.push(var);
val = substituteVariables(val, params, substituting);
substituting.pop();
} else {
val = m.group();
}
m.appendReplacement(sb, val.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\@", "\\\\\\@"));
}
m.appendTail(sb);
return sb.toString();
}
/**
* This class returns the original name of the artifact 'on demand'. This is done to avoid
* having to read the cached datafile containing the original location of the artifact if we
* don't need it.
*/
private static class OriginalArtifactNameValue {
// module properties
private String org;
private String moduleName;
private String branch;
private String revision;
private Map<String, String> extraModuleAttributes;
// artifact properties
private String artifactName;
private String artifactType;
private String artifactExt;
private Map<String, String> extraArtifactAttributes;
// cached origin;
private ArtifactOrigin origin;
public OriginalArtifactNameValue(String org, String moduleName, String branch,
String revision, String artifactName, String artifactType, String artifactExt,
Map<String, String> extraModuleAttributes, Map<String, String> extraArtifactAttributes) {
this.org = org;
this.moduleName = moduleName;
this.branch = branch;
this.revision = revision;
this.artifactName = artifactName;
this.artifactType = artifactType;
this.artifactExt = artifactExt;
this.extraModuleAttributes = extraModuleAttributes;
this.extraArtifactAttributes = extraArtifactAttributes;
}
/**
* @param origin ArtifactOrigin
*/
public OriginalArtifactNameValue(ArtifactOrigin origin) {
this.origin = origin;
}
// Called by substituteTokens only if the original artifact name is needed
public String toString() {
if (origin == null) {
ModuleRevisionId revId = ModuleRevisionId.newInstance(org, moduleName, branch,
revision, extraModuleAttributes);
Artifact artifact = new DefaultArtifact(revId, null, artifactName, artifactType,
artifactExt, extraArtifactAttributes);
// TODO cache: see how we could know which actual cache manager to use, since this
// will fail when using a resolver in a chain with a specific cache manager
RepositoryCacheManager cacheManager = IvyContext.getContext().getSettings()
.getResolver(revId).getRepositoryCacheManager();
origin = cacheManager.getSavedArtifactOrigin(artifact);
if (ArtifactOrigin.isUnknown(origin)) {
Message.debug("no artifact origin found for " + artifact + " in "
+ cacheManager);
return null;
}
}
if (ArtifactOrigin.isUnknown(origin)) {
return null;
}
// we assume that the original filename is the last part of the original file location
String location = origin.getLocation();
int lastPathIndex = location.lastIndexOf('/');
if (lastPathIndex == -1) {
lastPathIndex = location.lastIndexOf('\\');
}
int lastColonIndex = location.lastIndexOf('.');
return location.substring(lastPathIndex + 1, lastColonIndex);
}
}
public static String getTokenRoot(String pattern) {
int index = pattern.indexOf('[');
if (index == -1) {
return pattern;
} else {
// it could be that pattern is something like "lib/([optional]/)[module]"
// we don't want the '(' in the result
int optionalIndex = pattern.indexOf('(');
if (optionalIndex >= 0) {
index = Math.min(index, optionalIndex);
}
return pattern.substring(0, index);
}
}
public static String getFirstToken(String pattern) {
if (pattern == null) {
return null;
}
int startIndex = pattern.indexOf('[');
if (startIndex == -1) {
return null;
}
int endIndex = pattern.indexOf(']', startIndex);
if (endIndex == -1) {
return null;
}
return pattern.substring(startIndex + 1, endIndex);
}
}