/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.core.resolve;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
public class IvyNodeCallers {
public static class Caller {
private ModuleDescriptor md;
private ModuleRevisionId mrid;
private Map confs = new HashMap(); // Map (String callerConf -> String[] dependencyConfs)
private DependencyDescriptor dd;
private boolean callerCanExclude;
private boolean real = true;
public Caller(ModuleDescriptor md, ModuleRevisionId mrid, DependencyDescriptor dd,
boolean callerCanExclude) {
this.md = md;
this.mrid = mrid;
this.dd = dd;
this.callerCanExclude = callerCanExclude;
}
public void addConfiguration(String callerConf, String[] dependencyConfs) {
updateConfs(callerConf, dependencyConfs);
Configuration conf = md.getConfiguration(callerConf);
if (conf != null) {
String[] confExtends = conf.getExtends();
if (confExtends != null) {
for (int i = 0; i < confExtends.length; i++) {
addConfiguration(confExtends[i], dependencyConfs);
}
}
}
}
private void updateConfs(String callerConf, String[] dependencyConfs) {
String[] prevDepConfs = (String[]) confs.get(callerConf);
if (prevDepConfs != null) {
Set newDepConfs = new HashSet(Arrays.asList(prevDepConfs));
newDepConfs.addAll(Arrays.asList(dependencyConfs));
confs.put(callerConf, newDepConfs.toArray(new String[newDepConfs.size()]));
} else {
confs.put(callerConf, dependencyConfs);
}
}
public String[] getCallerConfigurations() {
return (String[]) confs.keySet().toArray(new String[confs.keySet().size()]);
}
public ModuleRevisionId getModuleRevisionId() {
return mrid;
}
public boolean equals(Object obj) {
if (!(obj instanceof Caller)) {
return false;
}
Caller other = (Caller) obj;
return other.confs.equals(confs) && mrid.equals(other.mrid);
}
public int hashCode() {
// CheckStyle:MagicNumber| OFF
int hash = 31;
hash = hash * 13 + confs.hashCode();
hash = hash * 13 + mrid.hashCode();
// CheckStyle:MagicNumber| ON
return hash;
}
public String toString() {
return mrid.toString();
}
public ModuleRevisionId getAskedDependencyId(ResolveData resolveData) {
return dd.getDependencyRevisionId();
}
public ModuleDescriptor getModuleDescriptor() {
return md;
}
public boolean canExclude() {
return callerCanExclude || md.canExclude() || dd.canExclude();
}
public DependencyDescriptor getDependencyDescriptor() {
return dd;
}
public void setRealCaller(boolean b) {
this.real = b;
}
public boolean isRealCaller() {
return real;
}
}
// Map (String rootModuleConf -> Map (ModuleRevisionId -> Caller)): key in second map is used to
// easily get a caller by its mrid
private Map callersByRootConf = new HashMap();
// this map contains all the module ids calling this one (including transitively) as keys.
// the mapped nodes (values) correspond to a direct caller from which the transitive caller
// comes
private Map allCallers = new HashMap(); // Map (ModuleId -> IvyNode)
private IvyNode node;
public IvyNodeCallers(IvyNode node) {
this.node = node;
}
/**
* @param rootModuleConf
* @param mrid
* @param callerConf
* @param dependencyConfs
* '*' must have been resolved
* @param dd
* the dependency revision id asked by the caller
*/
public void addCaller(String rootModuleConf, IvyNode callerNode, String callerConf,
String requestedConf, String[] dependencyConfs, DependencyDescriptor dd) {
ModuleDescriptor md = callerNode.getDescriptor();
ModuleRevisionId mrid = callerNode.getResolvedId();
if (mrid.getModuleId().equals(node.getId().getModuleId())) {
throw new IllegalArgumentException("a module is not authorized to depend on itself: "
+ node.getId());
}
Map callers = (Map) callersByRootConf.get(rootModuleConf);
if (callers == null) {
callers = new HashMap();
callersByRootConf.put(rootModuleConf, callers);
}
Caller caller = (Caller) callers.get(mrid);
if (caller == null) {
caller = new Caller(md, mrid, dd, callerNode.canExclude(rootModuleConf));
callers.put(mrid, caller);
}
caller.addConfiguration(requestedConf, dependencyConfs);
IvyNode parent = callerNode.getRealNode();
for (Iterator iter = parent.getAllCallersModuleIds().iterator(); iter.hasNext();) {
ModuleId mid = (ModuleId) iter.next();
allCallers.put(mid, parent);
}
allCallers.put(mrid.getModuleId(), callerNode);
}
void removeCaller(String rootModuleConf, ModuleRevisionId callerMrid) {
allCallers.remove(callerMrid.getModuleId());
Map callers = (Map) callersByRootConf.get(rootModuleConf);
if (callers != null) {
callers.remove(callerMrid);
}
}
public Caller[] getCallers(String rootModuleConf) {
Map callers = (Map) callersByRootConf.get(rootModuleConf);
if (callers == null) {
return new Caller[0];
}
return (Caller[]) callers.values().toArray(new Caller[callers.values().size()]);
}
public Caller[] getAllCallers() {
Set all = new HashSet();
for (Iterator iter = callersByRootConf.values().iterator(); iter.hasNext();) {
Map callers = (Map) iter.next();
all.addAll(callers.values());
}
return (Caller[]) all.toArray(new Caller[all.size()]);
}
public Caller[] getAllRealCallers() {
Set all = new HashSet();
for (Iterator iter = callersByRootConf.values().iterator(); iter.hasNext();) {
Map callers = (Map) iter.next();
for (Iterator iterator = callers.values().iterator(); iterator.hasNext();) {
Caller c = (Caller) iterator.next();
if (c.isRealCaller()) {
all.add(c);
}
}
}
return (Caller[]) all.toArray(new Caller[all.size()]);
}
public Collection getAllCallersModuleIds() {
return allCallers.keySet();
}
void updateFrom(IvyNodeCallers callers, String rootModuleConf, boolean real) {
Map nodecallers = (Map) callers.callersByRootConf.get(rootModuleConf);
if (nodecallers != null) {
Map thiscallers = (Map) callersByRootConf.get(rootModuleConf);
if (thiscallers == null) {
thiscallers = new HashMap();
callersByRootConf.put(rootModuleConf, thiscallers);
}
for (Iterator iter = nodecallers.values().iterator(); iter.hasNext();) {
Caller caller = (Caller) iter.next();
if (!thiscallers.containsKey(caller.getModuleRevisionId())) {
if (!real) {
caller.setRealCaller(false);
}
thiscallers.put(caller.getModuleRevisionId(), caller);
}
}
}
}
public IvyNode getDirectCallerFor(ModuleId from) {
return (IvyNode) allCallers.get(from);
}
/**
* Returns true if ALL callers exclude the given artifact in the given root module conf
*
* @param rootModuleConf
* @param artifact
* @return
*/
boolean doesCallersExclude(String rootModuleConf, Artifact artifact) {
return doesCallersExclude(rootModuleConf, artifact, new Stack());
}
boolean doesCallersExclude(String rootModuleConf, Artifact artifact, Stack callersStack) {
callersStack.push(node.getId());
try {
Caller[] callers = getCallers(rootModuleConf);
if (callers.length == 0) {
return false;
}
boolean allUnconclusive = true;
for (int i = 0; i < callers.length; i++) {
if (!callers[i].canExclude()) {
return false;
}
ModuleDescriptor md = callers[i].getModuleDescriptor();
Boolean doesExclude = node.doesExclude(md, rootModuleConf,
callers[i].getCallerConfigurations(), callers[i].getDependencyDescriptor(),
artifact, callersStack);
if (doesExclude != null) {
if (!doesExclude.booleanValue()) {
return false;
}
allUnconclusive = false;
}
}
return allUnconclusive ? false : true;
} finally {
callersStack.pop();
}
}
}