| /* |
| * 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); |
| } |
| } |