blob: 40cef5f4a45f342488b761a15535d4edf69e1c8c [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.plugins.parser;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.ResourceHelper;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.util.Message;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import static org.apache.ivy.util.StringUtils.splitToArray;
public abstract class AbstractModuleDescriptorParser implements ModuleDescriptorParser {
public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
boolean validate) throws ParseException, IOException {
return parseDescriptor(ivySettings, descriptorURL, new URLResource(descriptorURL), validate);
}
public String getType() {
return "ivy";
}
public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
return DefaultArtifact.newIvyArtifact(mrid, new Date(res.getLastModified()));
}
protected abstract static class AbstractParser extends DefaultHandler {
private static final String DEFAULT_CONF_MAPPING = "*->*";
private String defaultConf; // used only as defaultconf, not used for
// guessing right side part of a mapping
private String defaultConfMapping; // same as default conf but is used
// for guessing right side part of a mapping
private DefaultDependencyDescriptor defaultConfMappingDescriptor;
private Resource res;
private List<String> errors = new ArrayList<>();
private DefaultModuleDescriptor md;
private ModuleDescriptorParser parser;
protected AbstractParser(ModuleDescriptorParser parser) {
this.parser = parser;
}
public ModuleDescriptorParser getModuleDescriptorParser() {
return parser;
}
protected void checkErrors() throws ParseException {
if (!errors.isEmpty()) {
throw new ParseException(errors.toString(), 0);
}
}
public void setResource(Resource res) {
this.res = res; // used for log and date only
md = new DefaultModuleDescriptor(parser, res);
md.setLastModified(ResourceHelper.getLastModifiedOrDefault(res));
}
protected Resource getResource() {
return res;
}
protected String getDefaultConfMapping() {
return defaultConfMapping;
}
protected void setDefaultConfMapping(String defaultConf) {
defaultConfMapping = defaultConf;
getMd().setDefaultConfMapping(defaultConf);
}
protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd) {
parseDepsConfs(confs, dd, defaultConfMapping != null);
}
protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand) {
parseDepsConfs(confs, dd, useDefaultMappingToGuessRightOperand, true);
}
protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand, boolean evaluateConditions) {
if (confs == null) {
return;
}
String[] conf = confs.trim().split("\\s*;\\s*");
parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperand, evaluateConditions);
}
protected void parseDepsConfs(String[] conf, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand) {
parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperand, true);
}
protected void parseDepsConfs(String[] confs, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand, boolean evaluateConditions) {
replaceConfigurationWildcards(md);
for (String conf : confs) {
String[] ops = conf.split("->");
switch (ops.length) {
case 1:
for (String modConf : splitToArray(ops[0])) {
if (useDefaultMappingToGuessRightOperand) {
String[] depConfs = getDefaultConfMappingDescriptor()
.getDependencyConfigurations(modConf);
if (depConfs.length > 0) {
for (String depConf : depConfs) {
String mappedDependency = evaluateConditions ? evaluateCondition(
depConf.trim(), dd) : depConf.trim();
if (mappedDependency != null) {
dd.addDependencyConfiguration(modConf,
mappedDependency);
}
}
} else {
// no default mapping found for this configuration, map
// configuration to itself
dd.addDependencyConfiguration(modConf, modConf);
}
} else {
dd.addDependencyConfiguration(modConf, modConf);
}
}
break;
case 2:
for (String modConf : splitToArray(ops[0])) {
for (String depConf : splitToArray(ops[1])) {
String mappedDependency = evaluateConditions ? evaluateCondition(
depConf, dd) : depConf;
if (mappedDependency != null) {
dd.addDependencyConfiguration(modConf, mappedDependency);
}
}
}
break;
default:
addError("invalid conf " + conf + " for " + dd);
break;
}
}
if (md.isMappingOverride()) {
addExtendingConfigurations(confs, dd, useDefaultMappingToGuessRightOperand);
}
}
/**
* Evaluate the optional condition in the given configuration, like "[org=MYORG]confX". If
* the condition evaluates to true, the configuration is returned, if the condition
* evaluates to false, null is returned. If there are no conditions, the configuration
* itself is returned.
*
* @param conf
* the configuration to evaluate
* @param dd
* the dependency descriptor to which the configuration will be added
* @return the evaluated condition
*/
private String evaluateCondition(String conf, DefaultDependencyDescriptor dd) {
if (conf.charAt(0) != '[') {
return conf;
}
int endConditionIndex = conf.indexOf(']');
if (endConditionIndex == -1) {
addError("invalid conf " + conf + " for " + dd);
return null;
}
String condition = conf.substring(1, endConditionIndex);
int notEqualIndex = condition.indexOf("!=");
if (notEqualIndex == -1) {
int equalIndex = condition.indexOf('=');
if (equalIndex == -1) {
addError("invalid conf " + conf + " for " + dd.getDependencyRevisionId());
return null;
}
String leftOp = condition.substring(0, equalIndex).trim();
String rightOp = condition.substring(equalIndex + 1).trim();
// allow organisation synonyms, like 'org' or 'organization'
if (leftOp.equals("org") || leftOp.equals("organization")) {
leftOp = "organisation";
}
String attrValue = dd.getAttribute(leftOp);
if (!rightOp.equals(attrValue)) {
return null;
}
} else {
String leftOp = condition.substring(0, notEqualIndex).trim();
String rightOp = condition.substring(notEqualIndex + 2).trim();
// allow organisation synonyms, like 'org' or 'organization'
if (leftOp.equals("org") || leftOp.equals("organization")) {
leftOp = "organisation";
}
String attrValue = dd.getAttribute(leftOp);
if (rightOp.equals(attrValue)) {
return null;
}
}
return conf.substring(endConditionIndex + 1);
}
private void addExtendingConfigurations(String[] confs, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand) {
for (String conf : confs) {
addExtendingConfigurations(conf, dd, useDefaultMappingToGuessRightOperand);
}
}
private void addExtendingConfigurations(String conf, DefaultDependencyDescriptor dd,
boolean useDefaultMappingToGuessRightOperand) {
Set<String> configsToAdd = new HashSet<>();
for (Configuration config : md.getConfigurations()) {
for (String ext : config.getExtends()) {
if (conf.equals(ext)) {
String configName = config.getName();
configsToAdd.add(configName);
addExtendingConfigurations(configName, dd,
useDefaultMappingToGuessRightOperand);
}
}
}
parseDepsConfs(configsToAdd.toArray(new String[configsToAdd.size()]),
dd, useDefaultMappingToGuessRightOperand);
}
protected DependencyDescriptor getDefaultConfMappingDescriptor() {
if (defaultConfMappingDescriptor == null) {
defaultConfMappingDescriptor = new DefaultDependencyDescriptor(
ModuleRevisionId.newInstance("", "", ""), false);
parseDepsConfs(defaultConfMapping, defaultConfMappingDescriptor, false, false);
}
return defaultConfMappingDescriptor;
}
protected void addError(String msg) {
if (res != null) {
errors.add(msg + " in " + res + "\n");
} else {
errors.add(msg + "\n");
}
}
@Override
public void warning(SAXParseException ex) {
Message.warn("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
}
@Override
public void error(SAXParseException ex) {
addError("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
}
@Override
public void fatalError(SAXParseException ex) throws SAXException {
addError("[Fatal Error] " + getLocationString(ex) + ": " + ex.getMessage());
}
/** Returns a string of the location. */
private String getLocationString(SAXParseException ex) {
StringBuilder str = new StringBuilder();
String systemId = ex.getSystemId();
if (systemId != null) {
int index = systemId.lastIndexOf('/');
if (index != -1) {
systemId = systemId.substring(index + 1);
}
str.append(systemId);
} else if (getResource() != null) {
str.append(getResource().toString());
}
str.append(':');
str.append(ex.getLineNumber());
str.append(':');
str.append(ex.getColumnNumber());
return str.toString();
} // getLocationString(SAXParseException):String
protected String getDefaultConf() {
return (defaultConf != null) ? defaultConf
: (defaultConfMapping != null) ? defaultConfMapping : DEFAULT_CONF_MAPPING;
}
protected void setDefaultConf(String defaultConf) {
this.defaultConf = defaultConf;
getMd().setDefaultConf(defaultConf);
}
public ModuleDescriptor getModuleDescriptor() throws ParseException {
checkErrors();
return md;
}
protected Date getDefaultPubDate() {
return new Date(md.getLastModified());
}
private void replaceConfigurationWildcards(ModuleDescriptor md) {
for (Configuration config : md.getConfigurations()) {
config.replaceWildcards(md);
}
}
protected void setMd(DefaultModuleDescriptor md) {
this.md = md;
}
protected DefaultModuleDescriptor getMd() {
return md;
}
}
}