blob: 532bcd2c9279e9e7d4f55e0cfe45cad2ff58e88a [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.xml;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyContext;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.ConfigurationAware;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
import org.apache.ivy.core.module.descriptor.DefaultExtendsDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultIncludeRule;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ExcludeRule;
import org.apache.ivy.core.module.descriptor.ExtraInfoHolder;
import org.apache.ivy.core.module.descriptor.IncludeRule;
import org.apache.ivy.core.module.descriptor.License;
import org.apache.ivy.core.module.descriptor.MDArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
import org.apache.ivy.core.module.id.ArtifactId;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.resolve.ResolveData;
import org.apache.ivy.core.resolve.ResolveEngine;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.resolve.ResolvedModuleRevision;
import org.apache.ivy.plugins.conflict.ConflictManager;
import org.apache.ivy.plugins.conflict.FixedConflictManager;
import org.apache.ivy.plugins.matcher.PatternMatcher;
import org.apache.ivy.plugins.namespace.NameSpaceHelper;
import org.apache.ivy.plugins.namespace.Namespace;
import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParser;
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
import org.apache.ivy.plugins.parser.ParserSettings;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.file.FileResource;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.util.DateUtil;
import org.apache.ivy.util.FileUtil;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItemHelper;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import static org.apache.ivy.core.module.descriptor.Configuration.Visibility.getVisibility;
import static org.apache.ivy.util.StringUtils.isNullOrEmpty;
import static org.apache.ivy.util.StringUtils.splitToArray;
/**
* Parses an xml ivy file and output a ModuleDescriptor. For dependency and performance reasons, it
* uses only the SAX API, which makes the parsing code harder to understand.
*/
public class XmlModuleDescriptorParser extends AbstractModuleDescriptorParser {
static final List<String> DEPENDENCY_REGULAR_ATTRIBUTES = Arrays.asList("org", "name", "branch",
"branchConstraint", "rev", "revConstraint", "force", "transitive", "changing", "conf");
private static final XmlModuleDescriptorParser INSTANCE = new XmlModuleDescriptorParser();
public static XmlModuleDescriptorParser getInstance() {
return INSTANCE;
}
protected XmlModuleDescriptorParser() {
}
/**
* @param ivySettings ParserSettings
* @param xmlURL
* the url pointing to the file to parse
* @param res
* the real resource to parse, used for log only
* @param validate boolean
* @return ModuleDescriptor
* @throws ParseException if something goes wrong
* @throws IOException if something goes wrong
*/
public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL xmlURL, Resource res,
boolean validate) throws ParseException, IOException {
Parser parser = newParser(ivySettings);
parser.setValidate(validate);
parser.setResource(res);
parser.setInput(xmlURL);
parser.parse();
return parser.getModuleDescriptor();
}
/** Used for test purpose */
ModuleDescriptor parseDescriptor(ParserSettings ivySettings, InputStream descriptor,
Resource res, boolean validate) throws ParseException {
Parser parser = newParser(ivySettings);
parser.setValidate(validate);
parser.setResource(res);
parser.setInput(descriptor);
parser.parse();
return parser.getModuleDescriptor();
}
/**
* Instantiates a Parser instance responsible for actual parsing of Ivy files.
* <p>
* Override this method if you want to use a custom Parser.
* </p>
*
* @param ivySettings
* the settings to use during parsing
* @return the Parser instance used for parsing Ivy files
*/
protected Parser newParser(ParserSettings ivySettings) {
return new Parser(this, ivySettings);
}
public boolean accept(Resource res) {
return true; // this the default parser, it thus accepts all resources
}
public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
throws IOException, ParseException {
try {
Namespace ns = null;
if (md instanceof DefaultModuleDescriptor) {
DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
ns = dmd.getNamespace();
}
XmlModuleDescriptorUpdater.update(
is,
res,
destFile,
new UpdateOptions().setSettings(IvyContext.getContext().getSettings())
.setStatus(md.getStatus())
.setRevision(md.getResolvedModuleRevisionId().getRevision())
.setPubdate(md.getResolvedPublicationDate()).setUpdateBranch(false)
.setNamespace(ns));
} catch (SAXException e) {
ParseException pe = new ParseException("exception occurred while parsing " + res, 0);
pe.initCause(e);
throw pe;
} finally {
if (is != null) {
is.close();
}
}
}
public static class Parser extends AbstractParser {
public static final class State {
public static final int NONE = 0;
public static final int INFO = 1;
public static final int CONF = 2;
public static final int PUB = 3;
public static final int DEP = 4;
public static final int DEP_ARTIFACT = 5;
public static final int ARTIFACT_INCLUDE = 6;
public static final int ARTIFACT_EXCLUDE = 7;
public static final int CONFLICT = 8;
public static final int EXCLUDE = 9;
public static final int DEPS = 10;
public static final int DESCRIPTION = 11;
public static final int EXTRA_INFO = 12;
private State() {
}
}
protected static final List<String> ALLOWED_VERSIONS = Arrays.asList("1.0",
"1.1", "1.2", "1.3", "1.4", "2.0", "2.1", "2.2", "2.3", "2.4");
private static final String IVY_XSD_CONTENT;
/* how and what do we have to parse */
private ParserSettings settings;
private boolean validate = true;
private URL descriptorURL;
private InputStream descriptorInput;
/* Parsing state */
private int state = State.NONE;
private PatternMatcher defaultMatcher;
private DefaultDependencyDescriptor dd;
private ConfigurationAware confAware;
private MDArtifact artifact;
private String conf;
private boolean artifactsDeclared = false;
private StringBuilder buffer;
private String descriptorVersion;
private String[] publicationsDefaultConf;
private Stack<ExtraInfoHolder> extraInfoStack = new Stack<>();
static {
String ivyXSDContent = null;
final InputStream is = Parser.class.getResourceAsStream("ivy.xsd");
if (is != null) {
final StringBuilder sb = new StringBuilder();
try {
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
if (sb.length() != 0) {
sb.append("\n");
}
sb.append(line);
}
} catch (UnsupportedEncodingException e) {
// ignore
ivyXSDContent = null;
} catch (IOException e) {
// ignore
ivyXSDContent = null;
}
} finally {
try {
is.close();
} catch (Exception e) {
// ignore
}
}
ivyXSDContent = sb.length() == 0 ? null : sb.toString();
}
IVY_XSD_CONTENT = ivyXSDContent;
}
public Parser(ModuleDescriptorParser parser, ParserSettings ivySettings) {
super(parser);
settings = ivySettings;
}
public void setInput(InputStream descriptorInput) {
this.descriptorInput = descriptorInput;
}
public void setInput(URL descriptorURL) {
this.descriptorURL = descriptorURL;
}
public void setValidate(boolean validate) {
this.validate = validate;
}
public void parse() throws ParseException {
try {
URL schemaURL = validate ? getSchemaURL() : null;
XMLHelper.ExternalResources e =
validate && System.getProperty(XMLHelper.EXTERNAL_RESOURCES) == null
? XMLHelper.ExternalResources.IGNORE
: XMLHelper.ExternalResources.fromSystemProperty();
if (descriptorURL != null) {
XMLHelper.parse(descriptorURL, schemaURL, this, null, e);
} else {
XMLHelper.parse(descriptorInput, schemaURL, this, null, e);
}
checkConfigurations();
replaceConfigurationWildcards();
getMd().setModuleArtifact(
DefaultArtifact.newIvyArtifact(getMd().getResolvedModuleRevisionId(), getMd()
.getPublicationDate()));
if (!artifactsDeclared) {
for (String config : getMd().getConfigurationsNames()) {
getMd().addArtifact(config, new MDArtifact(getMd(),
getMd().getModuleRevisionId().getName(), "jar", "jar"));
}
}
getMd().check();
} catch (ParserConfigurationException ex) {
throw new IllegalStateException(ex.getMessage() + " in "
+ descriptorURL, ex);
} catch (Exception ex) {
checkErrors();
ParseException pe = new ParseException(ex.getMessage() + " in " + descriptorURL, 0);
pe.initCause(ex);
throw pe;
}
}
@Override
public InputSource resolveEntity(final String publicId, final String systemId)
throws IOException, SAXException {
if (isApacheOrgIvyXSDSystemId(systemId) && IVY_XSD_CONTENT != null) {
// redirect the schema location to local file based ivy.xsd whose content
// we have already read and is available in-memory.
final InputSource source = new InputSource(new StringReader(IVY_XSD_CONTENT));
return source;
}
return super.resolveEntity(publicId, systemId);
}
private static boolean isApacheOrgIvyXSDSystemId(final String systemId) {
if (systemId == null) {
return false;
}
return systemId.equals("http://ant.apache.org/ivy/schemas/ivy.xsd")
|| systemId.equals("https://ant.apache.org/ivy/schemas/ivy.xsd");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
try {
if (state == State.DESCRIPTION) {
// make sure we don't interpret any tag while in description tag
getBuffer().append("<").append(qName);
for (int i = 0; i < attributes.getLength(); i++) {
getBuffer().append(" ");
getBuffer().append(attributes.getQName(i));
getBuffer().append("=\"");
getBuffer().append(attributes.getValue(i));
getBuffer().append("\"");
}
getBuffer().append(">");
} else if ("ivy-module".equals(qName)) {
ivyModuleStarted(attributes);
} else if ("info".equals(qName)) {
infoStarted(attributes);
} else if (state == State.INFO && "extends".equals(qName)) {
extendsStarted(attributes);
} else if (state == State.INFO && "license".equals(qName)) {
getMd().addLicense(
new License(settings.substitute(attributes.getValue("name")), settings
.substitute(attributes.getValue("url"))));
} else if (state == State.INFO && "description".equals(qName)) {
getMd().setHomePage(settings.substitute(attributes.getValue("homepage")));
state = State.DESCRIPTION;
buffer = new StringBuilder();
} else if (state == State.INFO && "ivyauthor".equals(qName)) {
// nothing to do, we don't store this
} else if (state == State.INFO && "repository".equals(qName)) {
// nothing to do, we don't store this
} else if (state == State.EXTRA_INFO || state == State.INFO
&& isOtherNamespace(qName)) {
buffer = new StringBuilder();
state = State.EXTRA_INFO;
ExtraInfoHolder extraInfo = new ExtraInfoHolder();
extraInfo.setName(qName);
for (int i = 0; i < attributes.getLength(); i++) {
extraInfo.getAttributes().put(attributes.getQName(i),
attributes.getValue(i));
}
extraInfoStack.push(extraInfo);
} else if ("configurations".equals(qName)) {
configurationStarted(attributes);
} else if ("publications".equals(qName)) {
publicationsStarted(attributes);
} else if ("dependencies".equals(qName)) {
dependenciesStarted(attributes);
} else if ("conflicts".equals(qName)) {
if (!descriptorVersion.startsWith("1.")) {
Message.deprecated("using conflicts section is deprecated: "
+ "please use hints section instead. Ivy file URL: "
+ descriptorURL);
}
state = State.CONFLICT;
checkConfigurations();
} else if ("artifact".equals(qName)) {
artifactStarted(qName, attributes);
} else if ("include".equals(qName) && state == State.DEP) {
addIncludeRule(qName, attributes);
} else if ("exclude".equals(qName) && state == State.DEP) {
addExcludeRule(qName, attributes);
} else if ("exclude".equals(qName) && state == State.DEPS) {
state = State.EXCLUDE;
parseRule(qName, attributes);
getMd().addExcludeRule((ExcludeRule) confAware);
} else if ("dependency".equals(qName)) {
dependencyStarted(attributes);
} else if ("conf".equals(qName)) {
confStarted(attributes);
} else if ("mapped".equals(qName)) {
dd.addDependencyConfiguration(conf,
settings.substitute(attributes.getValue("name")));
} else if ("conflict".equals(qName) && state == State.DEPS
|| "manager".equals(qName) && state == State.CONFLICT) {
managerStarted(attributes, state == State.CONFLICT ? "name" : "manager");
} else if ("override".equals(qName) && state == State.DEPS) {
mediationOverrideStarted(attributes);
} else if ("include".equals(qName) && state == State.CONF) {
includeConfStarted(attributes);
} else if (validate && state != State.EXTRA_INFO && state != State.DESCRIPTION) {
addError("unknown tag " + qName);
}
} catch (Exception ex) {
if (ex instanceof SAXException) {
throw (SAXException) ex;
}
throw new SAXException("Problem occurred while parsing ivy file: "
+ ex.getMessage(), ex);
}
}
/**
* Default parent location to check (for dev ONLY)
*
* @return a relative path to a parent module descriptor
*/
protected String getDefaultParentLocation() {
return "../ivy.xml";
}
/**
* Handle extends elements. It checks :
* <ul>
* <li>filesystem based on location attribute, if no one is specified it will check the
* default parent location</li>
* <li>cache to find a resolved parent descriptor</li>
* <li>ask repositories to retrieve the parent module descriptor</li>
* </ul>
*
* @param attributes Attributes
* @throws ParseException if something goes wrong
*/
protected void extendsStarted(Attributes attributes) throws ParseException {
String parentOrganisation = settings.substitute(attributes.getValue("organisation"));
String parentModule = settings.substitute(attributes.getValue("module"));
String parentRevision = (attributes.getValue("revision") == null) ? Ivy.getWorkingRevision()
: settings.substitute(attributes.getValue("revision"));
String location = (attributes.getValue("location") == null) ? getDefaultParentLocation()
: settings.substitute(attributes.getValue("location"));
String extendType = (attributes.getValue("extendType") == null) ? "all"
: settings.substitute(attributes.getValue("extendType").toLowerCase(Locale.US));
List<String> extendTypes = Arrays.asList(extendType.split(","));
ModuleId parentMid = new ModuleId(parentOrganisation, parentModule);
ModuleRevisionId parentMrid = new ModuleRevisionId(parentMid, parentRevision);
// check on filesystem based on location attribute (for dev ONLY)
ModuleDescriptor parent = null;
boolean local = false;
try {
parent = parseParentModuleOnFilesystem(location);
if (parent != null) {
ModuleId foundMid = parent.getResolvedModuleRevisionId().getModuleId();
if (!foundMid.equals(parentMid)) {
// the filesystem contains a parent module with different organisation
// or module name; ignore that parent module
Message.info("Found a parent module with unexpected ModuleRevisionId at source location "
+ location
+ "! Expected: "
+ parentMid
+ ". Found: "
+ foundMid
+ ". This parent module will be ignored.");
parent = null;
}
}
local = parent != null;
} catch (IOException e) {
Message.warn("Unable to parse included ivy file " + location, e);
}
// if not found, tries to resolve using repositories
if (parent == null) {
try {
parent = parseOtherIvyFile(parentMrid);
} catch (ParseException e) {
Message.warn("Unable to parse included ivy file for " + parentMrid.toString(),
e);
}
}
// if still not found throw an exception
if (parent == null) {
throw new ParseException("Unable to parse included ivy file for "
+ parentMrid.toString(), 0);
}
DefaultExtendsDescriptor ed = new DefaultExtendsDescriptor(parent, location,
extendTypes.toArray(new String[extendTypes.size()]), local);
getMd().addInheritedDescriptor(ed);
mergeWithOtherModuleDescriptor(extendTypes, parent);
}
/**
* Merge current module with a given module descriptor and specify what should be inherited
* through extendTypes argument
*
* @param extendTypes
* specify what should be inherited
* @param parent
* a given parent module descriptor
*/
protected void mergeWithOtherModuleDescriptor(List<String> extendTypes,
ModuleDescriptor parent) {
if (extendTypes.contains("all")) {
mergeAll(parent);
} else {
if (extendTypes.contains("info")) {
mergeInfo(parent);
}
if (extendTypes.contains("configurations")) {
mergeConfigurations(parent);
}
if (extendTypes.contains("dependencies")) {
mergeDependencies(parent.getDependencies());
}
if (extendTypes.contains("description")) {
mergeDescription(parent.getDescription());
}
if (extendTypes.contains("licenses")) {
mergeLicenses(parent.getLicenses());
}
if (extendTypes.contains("excludes")) {
mergeExcludes(parent.getAllExcludeRules());
}
}
}
/**
* Merge everything from a given parent
*
* @param parent
* a given parent module descriptor
*/
protected void mergeAll(ModuleDescriptor parent) {
mergeInfo(parent);
mergeConfigurations(parent);
mergeDependencies(parent.getDependencies());
mergeDescription(parent.getDescription());
mergeLicenses(parent.getLicenses());
mergeExcludes(parent.getAllExcludeRules());
}
/**
* Explain how to inherit metadata related to info element
*
* @param parent
* a given parent module descriptor
*/
protected void mergeInfo(ModuleDescriptor parent) {
ModuleRevisionId parentMrid = parent.getModuleRevisionId();
DefaultModuleDescriptor descriptor = getMd();
ModuleRevisionId currentMrid = descriptor.getModuleRevisionId();
ModuleRevisionId mergedMrid = ModuleRevisionId.newInstance(
mergeValue(parentMrid.getOrganisation(), currentMrid.getOrganisation()),
currentMrid.getName(),
mergeValue(parentMrid.getBranch(), currentMrid.getBranch()),
mergeRevisionValue(parentMrid.getRevision(), currentMrid.getRevision()),
mergeValues(parentMrid.getQualifiedExtraAttributes(),
currentMrid.getQualifiedExtraAttributes()));
descriptor.setModuleRevisionId(mergedMrid);
descriptor.setResolvedModuleRevisionId(mergedMrid);
descriptor.setStatus(mergeValue(parent.getStatus(), descriptor.getStatus()));
if (descriptor.getNamespace() == null && parent instanceof DefaultModuleDescriptor) {
Namespace parentNamespace = ((DefaultModuleDescriptor) parent).getNamespace();
descriptor.setNamespace(parentNamespace);
}
descriptor.getExtraInfos().addAll(parent.getExtraInfos());
}
private static String mergeRevisionValue(String inherited, String override) {
if (override == null || override.equals(Ivy.getWorkingRevision())) {
return inherited;
} else {
return override;
}
}
private static String mergeValue(String inherited, String override) {
return override == null ? inherited : override;
}
private static Map<String, String> mergeValues(Map<String, String> inherited,
Map<String, String> overrides) {
Map<String, String> dup = new LinkedHashMap<>(inherited.size()
+ overrides.size());
dup.putAll(inherited);
dup.putAll(overrides);
return dup;
}
/**
* Describes how to merge configurations elements
*
* @param parent
* the module descriptor
*/
protected void mergeConfigurations(ModuleDescriptor parent) {
ModuleRevisionId sourceMrid = parent.getModuleRevisionId();
for (Configuration configuration : parent.getConfigurations()) {
Message.debug("Merging configuration with: " + configuration.getName());
// copy configuration from parent descriptor
getMd().addConfiguration(new Configuration(configuration, sourceMrid));
}
if (parent instanceof DefaultModuleDescriptor) {
setDefaultConfMapping(((DefaultModuleDescriptor) parent).getDefaultConfMapping());
setDefaultConf(((DefaultModuleDescriptor) parent).getDefaultConf());
getMd().setMappingOverride(((DefaultModuleDescriptor) parent).isMappingOverride());
}
}
/**
* Describes how dependencies should be inherited
*
* @param dependencies
* array of dependencies to inherit
*/
protected void mergeDependencies(DependencyDescriptor[] dependencies) {
DefaultModuleDescriptor md = getMd();
for (DependencyDescriptor dependencyDescriptor : dependencies) {
Message.debug("Merging dependency with: "
+ dependencyDescriptor.getDependencyRevisionId().toString());
md.addDependency(dependencyDescriptor);
}
}
/**
* Describes how to merge description
*
* @param description
* description going to be inherited
*/
protected void mergeDescription(String description) {
String current = getMd().getDescription();
if (isNullOrEmpty(current)) {
getMd().setDescription(description);
}
}
/**
* Describes how to merge licenses
*
* @param licenses
* licenses going to be inherited
*/
public void mergeLicenses(License[] licenses) {
for (License license : licenses) {
getMd().addLicense(license);
}
}
/**
* Describes how to merge exclude rules
*
* @param excludeRules
* exclude rules going to be inherited
*/
public void mergeExcludes(ExcludeRule[] excludeRules) {
for (ExcludeRule excludeRule : excludeRules) {
getMd().addExcludeRule(excludeRule);
}
}
/**
* Returns the parent module using the location attribute (for dev purpose).
*
* @param location
* a given location
* @throws IOException if something goes wrong
* @throws ParseException if something goes wrong
*/
private ModuleDescriptor parseParentModuleOnFilesystem(String location) throws IOException,
ParseException {
if (!"file".equals(descriptorURL.getProtocol())) {
return null;
}
File file = new File(location);
if (!file.isAbsolute()) {
URL url = settings.getRelativeUrlResolver().getURL(descriptorURL, location);
try {
file = new File(new URI(url.toExternalForm()));
} catch (URISyntaxException e) {
file = new File(url.getPath());
}
}
file = FileUtil.normalize(file.getAbsolutePath());
if (!file.exists()) {
Message.verbose("Parent module doesn't exist on the filesystem: "
+ file.getAbsolutePath());
return null;
}
FileResource res = new FileResource(null, file);
ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(
res);
return parser.parseDescriptor(getSettings(), file.toURI().toURL(), res, isValidate());
}
/**
* Describe how to parse a {@link ModuleDescriptor} by asking repositories
*
* @param parentMrid
* a given {@link ModuleRevisionId} to find
* @return a {@link ModuleDescriptor} if found. Return null if no {@link ModuleDescriptor}
* was found
* @throws ParseException if something goes wrong
*/
protected ModuleDescriptor parseOtherIvyFile(ModuleRevisionId parentMrid)
throws ParseException {
Message.debug("Trying to parse included ivy file by asking repository for module :"
+ parentMrid.toString());
DependencyDescriptor dd = new DefaultDependencyDescriptor(parentMrid, true);
ResolveData data = IvyContext.getContext().getResolveData();
if (data == null) {
ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
ResolveOptions options = new ResolveOptions();
options.setDownload(false);
data = new ResolveData(engine, options);
}
DependencyResolver resolver = getSettings().getResolver(parentMrid);
dd = NameSpaceHelper.toSystem(dd, getSettings().getContextNamespace());
ResolvedModuleRevision otherModule = resolver.getDependency(dd, data);
if (otherModule == null) {
throw new ParseException("Unable to find " + parentMrid.toString(), 0);
}
return otherModule.getDescriptor();
}
protected void publicationsStarted(Attributes attributes) {
state = State.PUB;
artifactsDeclared = true;
checkConfigurations();
String defaultConf = settings.substitute(attributes.getValue("defaultconf"));
if (defaultConf != null) {
setPublicationsDefaultConf(defaultConf);
}
}
protected void setPublicationsDefaultConf(String defaultConf) {
this.publicationsDefaultConf = (defaultConf == null) ? null : splitToArray(defaultConf);
}
protected boolean isOtherNamespace(String qName) {
return qName.contains(":");
}
protected void managerStarted(Attributes attributes, String managerAtt) {
String org = settings.substitute(attributes.getValue("org"));
if (org == null) {
org = PatternMatcher.ANY_EXPRESSION;
}
String mod = settings.substitute(attributes.getValue("module"));
if (mod == null) {
mod = PatternMatcher.ANY_EXPRESSION;
}
ConflictManager cm;
String name = settings.substitute(attributes.getValue(managerAtt));
String rev = settings.substitute(attributes.getValue("rev"));
if (rev != null) {
cm = new FixedConflictManager(splitToArray(rev));
} else if (name != null) {
cm = settings.getConflictManager(name);
if (cm == null) {
addError("unknown conflict manager: " + name);
return;
}
} else {
addError("bad conflict manager: no manager nor rev");
return;
}
String matcherName = settings.substitute(attributes.getValue("matcher"));
PatternMatcher matcher = (matcherName == null) ? defaultMatcher
: settings.getMatcher(matcherName);
if (matcher == null) {
addError("unknown matcher: " + matcherName);
return;
}
getMd().addConflictManager(new ModuleId(org, mod), matcher, cm);
}
protected void mediationOverrideStarted(Attributes attributes) {
String org = settings.substitute(attributes.getValue("org"));
if (org == null) {
org = PatternMatcher.ANY_EXPRESSION;
}
String mod = settings.substitute(attributes.getValue("module"));
if (mod == null) {
mod = PatternMatcher.ANY_EXPRESSION;
}
String rev = settings.substitute(attributes.getValue("rev"));
String branch = settings.substitute(attributes.getValue("branch"));
String matcherName = settings.substitute(attributes.getValue("matcher"));
PatternMatcher matcher = (matcherName == null) ? defaultMatcher
: settings.getMatcher(matcherName);
if (matcher == null) {
addError("unknown matcher: " + matcherName);
return;
}
getMd().addDependencyDescriptorMediator(new ModuleId(org, mod), matcher,
new OverrideDependencyDescriptorMediator(branch, rev));
}
protected void includeConfStarted(Attributes attributes) throws SAXException, IOException,
ParserConfigurationException, ParseException {
URL url = settings.getRelativeUrlResolver().getURL(descriptorURL,
settings.substitute(attributes.getValue("file")),
settings.substitute(attributes.getValue("url")));
if (url == null) {
throw new SAXException("include tag must have a file or an url attribute");
}
// create a new temporary parser to read the configurations from
// the specified file.
Parser parser = new Parser(getModuleDescriptorParser(), settings);
parser.setInput(url);
parser.setMd(new DefaultModuleDescriptor(getModuleDescriptorParser(),
new URLResource(url)));
XMLHelper.parse(url, null, parser);
// add the configurations from this temporary parser to this module descriptor
for (Configuration config : parser.getModuleDescriptor().getConfigurations()) {
getMd().addConfiguration(config);
}
if (parser.getDefaultConfMapping() != null) {
Message.debug("setting default conf mapping from imported configurations file: "
+ parser.getDefaultConfMapping());
setDefaultConfMapping(parser.getDefaultConfMapping());
}
if (parser.getDefaultConf() != null) {
Message.debug("setting default conf from imported configurations file: "
+ parser.getDefaultConf());
setDefaultConf(parser.getDefaultConf());
}
if (parser.getMd().isMappingOverride()) {
Message.debug("enabling mapping-override from imported configurations" + " file");
getMd().setMappingOverride(true);
}
}
protected void confStarted(Attributes attributes) {
String conf = settings.substitute(attributes.getValue("name"));
switch (state) {
case State.CONF:
String visibility = settings.substitute(attributes.getValue("visibility"));
String ext = settings.substitute(attributes.getValue("extends"));
String transitiveValue = attributes.getValue("transitive");
boolean transitive = (transitiveValue == null)
|| Boolean.valueOf(attributes.getValue("transitive"));
String deprecated = attributes.getValue("deprecated");
Configuration configuration = new Configuration(conf,
getVisibility((visibility == null) ? "public" : visibility),
settings.substitute(attributes.getValue("description")),
(ext == null) ? null : ext.split(","), transitive, deprecated);
ExtendableItemHelper.fillExtraAttributes(settings, configuration, attributes,
Arrays.asList("name", "visibility", "extends", "transitive", "description",
"deprecated"));
getMd().addConfiguration(configuration);
break;
case State.PUB:
if ("*".equals(conf)) {
for (String config : getMd().getConfigurationsNames()) {
artifact.addConfiguration(config);
getMd().addArtifact(config, artifact);
}
} else {
artifact.addConfiguration(conf);
getMd().addArtifact(conf, artifact);
}
break;
case State.DEP:
this.conf = conf;
String mappeds = settings.substitute(attributes.getValue("mapped"));
if (mappeds != null) {
for (String mapped : splitToArray(mappeds)) {
dd.addDependencyConfiguration(conf, mapped);
}
}
break;
case State.DEP_ARTIFACT:
case State.ARTIFACT_INCLUDE:
case State.ARTIFACT_EXCLUDE:
addConfiguration(conf);
break;
default:
if (validate) {
addError("conf tag found in invalid tag: " + state);
}
break;
}
}
protected void dependencyStarted(Attributes attributes) {
state = State.DEP;
String org = settings.substitute(attributes.getValue("org"));
if (org == null) {
org = getMd().getModuleRevisionId().getOrganisation();
}
boolean force = Boolean.valueOf(settings.substitute(attributes.getValue("force")));
boolean changing = Boolean.valueOf(settings.substitute(attributes.getValue("changing")));
String transitiveValue = settings.substitute(attributes.getValue("transitive"));
boolean transitive = (transitiveValue == null)
|| Boolean.valueOf(attributes.getValue("transitive"));
String name = settings.substitute(attributes.getValue("name"));
String branch = settings.substitute(attributes.getValue("branch"));
String branchConstraint = settings.substitute(attributes.getValue("branchConstraint"));
/* if (branchConstraint == null) {
* // there was no branch constraint before, so we should
* // set the branchConstraint to the current default branch
* branchConstraint = settings.getDefaultBranch(ModuleId.newInstance(org, name));
* }
*/
String rev = settings.substitute(attributes.getValue("rev"));
String revConstraint = settings.substitute(attributes.getValue("revConstraint"));
Map<String, String> extraAttributes = ExtendableItemHelper.getExtraAttributes(settings,
attributes, DEPENDENCY_REGULAR_ATTRIBUTES);
ModuleRevisionId revId = ModuleRevisionId.newInstance(org, name, branch, rev,
extraAttributes);
ModuleRevisionId dynamicId = null;
if (revConstraint == null && branchConstraint == null) {
// no dynamic constraints defined, so dynamicId equals revId
dynamicId = ModuleRevisionId.newInstance(org, name, branch, rev, extraAttributes,
false);
} else {
if (branchConstraint == null) {
// this situation occurs when there was no branch defined
// in the original dependency descriptor. So the dynamicId
// shouldn't contain a branch neither
dynamicId = ModuleRevisionId.newInstance(org, name, null, revConstraint,
extraAttributes, false);
} else {
dynamicId = ModuleRevisionId.newInstance(org, name, branchConstraint,
revConstraint, extraAttributes);
}
}
dd = new DefaultDependencyDescriptor(getMd(), revId, dynamicId, force, changing,
transitive);
getMd().addDependency(dd);
String confs = settings.substitute(attributes.getValue("conf"));
if (confs != null && confs.length() > 0) {
parseDepsConfs(confs, dd);
}
}
protected void artifactStarted(String qName, Attributes attributes)
throws MalformedURLException {
if (state == State.PUB) {
// this is a published artifact
String artName = settings.substitute(attributes.getValue("name"));
if (artName == null) {
artName = getMd().getModuleRevisionId().getName();
}
String type = settings.substitute(attributes.getValue("type"));
if (type == null) {
type = "jar";
}
String ext = settings.substitute(attributes.getValue("ext"));
if (ext == null) {
ext = type;
}
String url = settings.substitute(attributes.getValue("url"));
artifact = new MDArtifact(getMd(), artName, type, ext, url == null ? null
: new URL(url), ExtendableItemHelper.getExtraAttributes(settings,
attributes, Arrays.asList("ext", "type", "name", "conf")));
String confs = settings.substitute(attributes.getValue("conf"));
// only add confs if they are specified. if they aren't, endElement will
// handle this
// only if there are no conf defined in sub elements
if (confs != null && confs.length() > 0) {
String[] configs = "*".equals(confs) ? getMd().getConfigurationsNames()
: splitToArray(confs);
for (String config : configs) {
artifact.addConfiguration(config);
getMd().addArtifact(config, artifact);
}
}
} else if (state == State.DEP) {
// this is an artifact asked for a particular dependency
addDependencyArtifacts(qName, attributes);
} else if (validate) {
addError("artifact tag found in invalid tag: " + state);
}
}
protected void dependenciesStarted(Attributes attributes) {
state = State.DEPS;
String defaultConf = settings.substitute(attributes.getValue("defaultconf"));
if (defaultConf != null) {
setDefaultConf(defaultConf);
}
defaultConf = settings.substitute(attributes.getValue("defaultconfmapping"));
if (defaultConf != null) {
setDefaultConfMapping(defaultConf);
}
String confMappingOverride = settings.substitute(attributes
.getValue("confmappingoverride"));
if (confMappingOverride != null) {
getMd().setMappingOverride(Boolean.valueOf(confMappingOverride));
}
checkConfigurations();
}
protected void configurationStarted(Attributes attributes) {
state = State.CONF;
setDefaultConfMapping(settings.substitute(attributes.getValue("defaultconfmapping")));
setDefaultConf(settings.substitute(attributes.getValue("defaultconf")));
getMd().setMappingOverride(
Boolean.valueOf(settings.substitute(attributes.getValue("confmappingoverride"))));
}
protected void infoStarted(Attributes attributes) {
state = State.INFO;
String org = settings.substitute(attributes.getValue("organisation"));
String module = settings.substitute(attributes.getValue("module"));
String revision = settings.substitute(attributes.getValue("revision"));
String branch = settings.substitute(attributes.getValue("branch"));
getMd().setModuleRevisionId(
ModuleRevisionId.newInstance(
org,
module,
branch,
revision,
ExtendableItemHelper.getExtraAttributes(settings, attributes, Arrays.asList(
"organisation", "module", "revision", "status", "publication",
"branch", "namespace", "default", "resolver"))));
String namespace = settings.substitute(attributes.getValue("namespace"));
if (namespace != null) {
Namespace ns = settings.getNamespace(namespace);
if (ns == null) {
Message.warn("namespace not found for " + getMd().getModuleRevisionId() + ": "
+ namespace);
} else {
getMd().setNamespace(ns);
}
}
String status = settings.substitute(attributes.getValue("status"));
getMd().setStatus(
status == null ? settings.getStatusManager().getDefaultStatus() : status);
getMd().setDefault(
Boolean.valueOf(settings.substitute(attributes.getValue("default"))));
String pubDate = settings.substitute(attributes.getValue("publication"));
if (pubDate != null && pubDate.length() > 0) {
try {
getMd().setPublicationDate(DateUtil.parse(pubDate));
} catch (ParseException e) {
addError("invalid publication date format: " + pubDate);
getMd().setPublicationDate(getDefaultPubDate());
}
} else {
getMd().setPublicationDate(getDefaultPubDate());
}
}
protected void ivyModuleStarted(Attributes attributes) throws SAXException {
descriptorVersion = attributes.getValue("version");
int versionIndex = ALLOWED_VERSIONS.indexOf(descriptorVersion);
if (versionIndex == -1) {
addError("invalid version " + descriptorVersion);
throw new SAXException("invalid version " + descriptorVersion);
}
if (versionIndex >= ALLOWED_VERSIONS.indexOf("1.3")) {
Message.debug("post 1.3 ivy file: using " + PatternMatcher.EXACT
+ " as default matcher");
defaultMatcher = settings.getMatcher(PatternMatcher.EXACT);
} else {
Message.debug("pre 1.3 ivy file: using " + PatternMatcher.EXACT_OR_REGEXP
+ " as default matcher");
defaultMatcher = settings.getMatcher(PatternMatcher.EXACT_OR_REGEXP);
}
for (int i = 0; i < attributes.getLength(); i++) {
if (attributes.getQName(i).startsWith("xmlns:")) {
getMd().addExtraAttributeNamespace(
attributes.getQName(i).substring("xmlns:".length()), attributes.getValue(i));
}
}
}
protected void addDependencyArtifacts(String tag, Attributes attributes)
throws MalformedURLException {
state = State.DEP_ARTIFACT;
parseRule(tag, attributes);
}
protected void addIncludeRule(String tag, Attributes attributes)
throws MalformedURLException {
state = State.ARTIFACT_INCLUDE;
parseRule(tag, attributes);
}
protected void addExcludeRule(String tag, Attributes attributes)
throws MalformedURLException {
state = State.ARTIFACT_EXCLUDE;
parseRule(tag, attributes);
}
protected void parseRule(String tag, Attributes attributes) throws MalformedURLException {
String name = settings.substitute(attributes.getValue("name"));
if (name == null) {
name = settings.substitute(attributes.getValue("artifact"));
if (name == null) {
name = "artifact".equals(tag) ? dd.getDependencyId().getName()
: PatternMatcher.ANY_EXPRESSION;
}
}
String type = settings.substitute(attributes.getValue("type"));
if (type == null) {
type = "artifact".equals(tag) ? "jar" : PatternMatcher.ANY_EXPRESSION;
}
String ext = settings.substitute(attributes.getValue("ext"));
if (ext == null) {
ext = type;
}
switch (state) {
case State.DEP_ARTIFACT: {
String url = settings.substitute(attributes.getValue("url"));
Map<String, String> extraAtt = ExtendableItemHelper.getExtraAttributes(settings,
attributes, Arrays.asList("name", "type", "ext", "url", "conf"));
confAware = new DefaultDependencyArtifactDescriptor(dd, name, type, ext,
url == null ? null : new URL(url), extraAtt);
break;
}
case State.ARTIFACT_INCLUDE: {
PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
String org = settings.substitute(attributes.getValue("org"));
if (org == null) {
org = PatternMatcher.ANY_EXPRESSION;
}
String module = settings.substitute(attributes.getValue("module"));
if (module == null) {
module = PatternMatcher.ANY_EXPRESSION;
}
ArtifactId aid = new ArtifactId(new ModuleId(org, module), name, type, ext);
Map<String, String> extraAtt = ExtendableItemHelper.getExtraAttributes(settings,
attributes, Arrays.asList("org", "module", "name", "type", "ext", "matcher",
"conf"));
confAware = new DefaultIncludeRule(aid, matcher, extraAtt);
break;
}
default: { // _state == ARTIFACT_EXCLUDE || EXCLUDE
PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
String org = settings.substitute(attributes.getValue("org"));
if (org == null) {
org = PatternMatcher.ANY_EXPRESSION;
}
String module = settings.substitute(attributes.getValue("module"));
if (module == null) {
module = PatternMatcher.ANY_EXPRESSION;
}
ArtifactId aid = new ArtifactId(new ModuleId(org, module), name, type, ext);
Map<String, String> extraAtt = ExtendableItemHelper.getExtraAttributes(settings,
attributes, Arrays.asList("org", "module", "name", "type", "ext", "matcher",
"conf"));
confAware = new DefaultExcludeRule(aid, matcher, extraAtt);
break;
}
}
String confs = settings.substitute(attributes.getValue("conf"));
// only add confs if they are specified. if they aren't, endElement will handle this
// only if there are no conf defined in sub elements
if (confs != null && confs.length() > 0) {
String[] configs = "*".equals(confs) ? getMd().getConfigurationsNames()
: splitToArray(confs);
for (String config : configs) {
addConfiguration(config);
}
}
}
protected void addConfiguration(String c) {
confAware.addConfiguration(c);
if (state == State.EXCLUDE) {
// we are adding a configuration to a module wide exclude rule we have nothing
// special to do here, the rule has already been added to the module descriptor
} else {
// we are currently adding a configuration to either an include, exclude or artifact
// element of a dependency. This means that we have to add this element to the
// corresponding conf of the current dependency descriptor
if (confAware instanceof DependencyArtifactDescriptor) {
dd.addDependencyArtifact(c, (DependencyArtifactDescriptor) confAware);
} else if (confAware instanceof IncludeRule) {
dd.addIncludeRule(c, (IncludeRule) confAware);
} else if (confAware instanceof ExcludeRule) {
dd.addExcludeRule(c, (ExcludeRule) confAware);
}
}
}
protected PatternMatcher getPatternMatcher(String m) {
String matcherName = settings.substitute(m);
PatternMatcher matcher = matcherName == null ? defaultMatcher : settings
.getMatcher(matcherName);
if (matcher == null) {
throw new IllegalArgumentException("unknown matcher " + matcherName);
}
return matcher;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (buffer != null) {
buffer.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (state == State.PUB && "artifact".equals(qName)
&& artifact.getConfigurations().length == 0) {
String[] configs = (publicationsDefaultConf == null)
? getMd().getConfigurationsNames() : publicationsDefaultConf;
for (String config : configs) {
artifact.addConfiguration(config);
getMd().addArtifact(config, artifact);
}
} else if ("configurations".equals(qName)) {
checkConfigurations();
} else if (state == State.DEP_ARTIFACT && "artifact".equals(qName)
|| state == State.ARTIFACT_INCLUDE && "include".equals(qName)
|| state == State.ARTIFACT_EXCLUDE && "exclude".equals(qName)) {
state = State.DEP;
if (confAware.getConfigurations().length == 0) {
for (String config : getMd().getConfigurationsNames()) {
addConfiguration(config);
}
}
confAware = null;
} else if ("exclude".equals(qName) && state == State.EXCLUDE) {
if (confAware.getConfigurations().length == 0) {
for (String config : getMd().getConfigurationsNames()) {
addConfiguration(config);
}
}
confAware = null;
state = State.DEPS;
} else if ("dependency".equals(qName) && state == State.DEP) {
if (dd.getModuleConfigurations().length == 0) {
parseDepsConfs(getDefaultConf(), dd);
}
state = State.DEPS;
} else if ("dependencies".equals(qName) && state == State.DEPS) {
state = State.NONE;
} else if (state == State.INFO && "info".equals(qName)) {
state = State.NONE;
} else if (state == State.DESCRIPTION && "description".equals(qName)) {
getMd().setDescription(buffer == null ? "" : buffer.toString().trim());
buffer = null;
state = State.INFO;
} else if (state == State.EXTRA_INFO) {
String content = buffer == null ? "" : buffer.toString();
buffer = null;
ExtraInfoHolder extraInfo = extraInfoStack.pop();
extraInfo.setContent(content);
if (extraInfoStack.isEmpty()) {
getMd().addExtraInfo(extraInfo);
state = State.INFO;
} else {
ExtraInfoHolder parentHolder = extraInfoStack.peek();
parentHolder.getNestedExtraInfoHolder().add(extraInfo);
}
} else if (state == State.DESCRIPTION) {
if (buffer.toString().endsWith("<" + qName + ">")) {
buffer.deleteCharAt(buffer.length() - 1);
buffer.append("/>");
} else {
buffer.append("</").append(qName).append(">");
}
}
}
protected void checkConfigurations() {
if (getMd().getConfigurations().length == 0) {
getMd().addConfiguration(new Configuration("default"));
}
}
protected void replaceConfigurationWildcards() {
for (Configuration config : getMd().getConfigurations()) {
config.replaceWildcards(getMd());
}
}
/* getters and setters available for extension only */
protected ParserSettings getSettings() {
return settings;
}
protected URL getDescriptorURL() {
return descriptorURL;
}
protected InputStream getDescriptorInput() {
return descriptorInput;
}
protected int getState() {
return state;
}
protected void setState(int state) {
this.state = state;
}
protected PatternMatcher getDefaultMatcher() {
return defaultMatcher;
}
protected DefaultDependencyDescriptor getDd() {
return dd;
}
protected void setDd(DefaultDependencyDescriptor dd) {
this.dd = dd;
}
protected ConfigurationAware getConfAware() {
return confAware;
}
protected void setConfAware(ConfigurationAware confAware) {
this.confAware = confAware;
}
protected MDArtifact getArtifact() {
return artifact;
}
protected void setArtifact(MDArtifact artifact) {
this.artifact = artifact;
}
protected String getConf() {
return conf;
}
protected void setConf(String conf) {
this.conf = conf;
}
protected boolean isArtifactsDeclared() {
return artifactsDeclared;
}
protected void setArtifactsDeclared(boolean artifactsDeclared) {
this.artifactsDeclared = artifactsDeclared;
}
protected StringBuilder getBuffer() {
return buffer;
}
protected void setBuffer(StringBuilder buffer) {
this.buffer = buffer;
}
protected String getDescriptorVersion() {
return descriptorVersion;
}
protected void setDescriptorVersion(String descriptorVersion) {
this.descriptorVersion = descriptorVersion;
}
protected String[] getPublicationsDefaultConf() {
return publicationsDefaultConf;
}
protected void setPublicationsDefaultConf(String[] publicationsDefaultConf) {
this.publicationsDefaultConf = publicationsDefaultConf;
}
protected boolean isValidate() {
return validate;
}
protected URL getSchemaURL() {
return getClass().getResource("ivy.xsd");
}
}
@Override
public String toString() {
return "ivy parser";
}
}