001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.core.resolve;
019:
020: import java.util.Arrays;
021: import java.util.Collection;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.Stack;
028:
029: import org.apache.ivy.core.module.descriptor.Artifact;
030: import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
031: import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
032: import org.apache.ivy.core.module.id.ModuleId;
033: import org.apache.ivy.core.module.id.ModuleRevisionId;
034:
035: public class IvyNodeCallers {
036: public static class Caller {
037: private ModuleDescriptor md;
038:
039: private ModuleRevisionId mrid;
040:
041: private Map confs = new HashMap(); // Map (String callerConf -> String[] dependencyConfs)
042:
043: private DependencyDescriptor dd;
044:
045: private boolean callerCanExclude;
046:
047: private boolean real = true;
048:
049: public Caller(ModuleDescriptor md, ModuleRevisionId mrid,
050: DependencyDescriptor dd, boolean callerCanExclude) {
051: this .md = md;
052: this .mrid = mrid;
053: this .dd = dd;
054: this .callerCanExclude = callerCanExclude;
055: }
056:
057: public void addConfiguration(String callerConf,
058: String[] dependencyConfs) {
059: String[] prevDepConfs = (String[]) confs.get(callerConf);
060: if (prevDepConfs != null) {
061: Set newDepConfs = new HashSet(Arrays
062: .asList(prevDepConfs));
063: newDepConfs.addAll(Arrays.asList(dependencyConfs));
064: confs.put(callerConf, (String[]) newDepConfs
065: .toArray(new String[newDepConfs.size()]));
066: } else {
067: confs.put(callerConf, dependencyConfs);
068: }
069: }
070:
071: public String[] getCallerConfigurations() {
072: return (String[]) confs.keySet().toArray(
073: new String[confs.keySet().size()]);
074: }
075:
076: public ModuleRevisionId getModuleRevisionId() {
077: return mrid;
078: }
079:
080: public boolean equals(Object obj) {
081: if (!(obj instanceof Caller)) {
082: return false;
083: }
084: Caller other = (Caller) obj;
085: return other.confs.equals(confs) && mrid.equals(other.mrid);
086: }
087:
088: public int hashCode() {
089: //CheckStyle:MagicNumber| OFF
090: int hash = 31;
091: hash = hash * 13 + confs.hashCode();
092: hash = hash * 13 + mrid.hashCode();
093: //CheckStyle:MagicNumber| ON
094: return hash;
095: }
096:
097: public String toString() {
098: return mrid.toString();
099: }
100:
101: public ModuleRevisionId getAskedDependencyId() {
102: return dd.getDependencyRevisionId();
103: }
104:
105: public ModuleDescriptor getModuleDescriptor() {
106: return md;
107: }
108:
109: public boolean canExclude() {
110: return callerCanExclude || md.canExclude()
111: || dd.canExclude();
112: }
113:
114: public DependencyDescriptor getDependencyDescriptor() {
115: return dd;
116: }
117:
118: public void setRealCaller(boolean b) {
119: this .real = b;
120: }
121:
122: public boolean isRealCaller() {
123: return real;
124: }
125: }
126:
127: // Map (String rootModuleConf -> Map (ModuleRevisionId -> Caller)): key in second map is used to
128: // easily get a caller by its mrid
129: private Map callersByRootConf = new HashMap();
130:
131: // this map contains all the module ids calling this one (including transitively) as keys
132: // the mapped nodes (values) correspond to a direct caller from which the transitive caller
133: // comes
134:
135: private Map allCallers = new HashMap(); // Map (ModuleId -> IvyNode)
136:
137: private IvyNode node;
138:
139: public IvyNodeCallers(IvyNode node) {
140: this .node = node;
141: }
142:
143: /**
144: * @param rootModuleConf
145: * @param mrid
146: * @param callerConf
147: * @param dependencyConfs
148: * '*' must have been resolved
149: * @param dd
150: * the dependency revision id asked by the caller
151: */
152: public void addCaller(String rootModuleConf, IvyNode callerNode,
153: String callerConf, String[] dependencyConfs,
154: DependencyDescriptor dd) {
155: ModuleDescriptor md = callerNode.getDescriptor();
156: ModuleRevisionId mrid = callerNode.getResolvedId();
157: if (mrid.getModuleId().equals(node.getId().getModuleId())) {
158: throw new IllegalArgumentException(
159: "a module is not authorized to depend on itself: "
160: + node.getId());
161: }
162: Map callers = (Map) callersByRootConf.get(rootModuleConf);
163: if (callers == null) {
164: callers = new HashMap();
165: callersByRootConf.put(rootModuleConf, callers);
166: }
167: Caller caller = (Caller) callers.get(mrid);
168: if (caller == null) {
169: caller = new Caller(md, mrid, dd, callerNode
170: .canExclude(rootModuleConf));
171: callers.put(mrid, caller);
172: }
173: caller.addConfiguration(callerConf, dependencyConfs);
174:
175: IvyNode parent = callerNode.getRealNode();
176: for (Iterator iter = parent.getAllCallersModuleIds().iterator(); iter
177: .hasNext();) {
178: ModuleId mid = (ModuleId) iter.next();
179: allCallers.put(mid, parent);
180: }
181: allCallers.put(mrid.getModuleId(), callerNode);
182: }
183:
184: void removeCaller(String rootModuleConf, ModuleRevisionId callerMrid) {
185: allCallers.remove(callerMrid.getModuleId());
186: Map callers = (Map) callersByRootConf.get(rootModuleConf);
187: if (callers != null) {
188: callers.remove(callerMrid);
189: }
190: }
191:
192: public Caller[] getCallers(String rootModuleConf) {
193: Map callers = (Map) callersByRootConf.get(rootModuleConf);
194: if (callers == null) {
195: return new Caller[0];
196: }
197: return (Caller[]) callers.values().toArray(
198: new Caller[callers.values().size()]);
199: }
200:
201: public Caller[] getAllCallers() {
202: Set all = new HashSet();
203: for (Iterator iter = callersByRootConf.values().iterator(); iter
204: .hasNext();) {
205: Map callers = (Map) iter.next();
206: all.addAll(callers.values());
207: }
208: return (Caller[]) all.toArray(new Caller[all.size()]);
209: }
210:
211: public Caller[] getAllRealCallers() {
212: Set all = new HashSet();
213: for (Iterator iter = callersByRootConf.values().iterator(); iter
214: .hasNext();) {
215: Map callers = (Map) iter.next();
216: for (Iterator iterator = callers.values().iterator(); iterator
217: .hasNext();) {
218: Caller c = (Caller) iterator.next();
219: if (c.isRealCaller()) {
220: all.add(c);
221: }
222: }
223: }
224: return (Caller[]) all.toArray(new Caller[all.size()]);
225: }
226:
227: public Collection getAllCallersModuleIds() {
228: return allCallers.keySet();
229: }
230:
231: void updateFrom(IvyNodeCallers callers, String rootModuleConf,
232: boolean real) {
233: Map nodecallers = (Map) callers.callersByRootConf
234: .get(rootModuleConf);
235: if (nodecallers != null) {
236: Map this callers = (Map) callersByRootConf
237: .get(rootModuleConf);
238: if (this callers == null) {
239: this callers = new HashMap();
240: callersByRootConf.put(rootModuleConf, this callers);
241: }
242: for (Iterator iter = nodecallers.values().iterator(); iter
243: .hasNext();) {
244: Caller caller = (Caller) iter.next();
245: if (!this callers.containsKey(caller
246: .getModuleRevisionId())) {
247: if (!real) {
248: caller.setRealCaller(false);
249: }
250: this callers.put(caller.getModuleRevisionId(),
251: caller);
252: }
253: }
254: }
255: }
256:
257: public IvyNode getDirectCallerFor(ModuleId from) {
258: return (IvyNode) allCallers.get(from);
259: }
260:
261: /**
262: * Returns true if ALL callers exclude the given artifact in the given root module conf
263: *
264: * @param rootModuleConf
265: * @param artifact
266: * @return
267: */
268: boolean doesCallersExclude(String rootModuleConf, Artifact artifact) {
269: return doesCallersExclude(rootModuleConf, artifact, new Stack());
270: }
271:
272: boolean doesCallersExclude(String rootModuleConf,
273: Artifact artifact, Stack callersStack) {
274: if (callersStack.contains(node.getId())) {
275: return false;
276: }
277: callersStack.push(node.getId());
278: try {
279: Caller[] callers = getCallers(rootModuleConf);
280: if (callers.length == 0) {
281: return false;
282: }
283: for (int i = 0; i < callers.length; i++) {
284: if (!callers[i].canExclude()) {
285: return false;
286: }
287: ModuleDescriptor md = callers[i].getModuleDescriptor();
288: if (!doesExclude(md, rootModuleConf, callers[i]
289: .getCallerConfigurations(), callers[i]
290: .getDependencyDescriptor(), artifact,
291: callersStack)) {
292: return false;
293: }
294: }
295: return true;
296: } finally {
297: callersStack.pop();
298: }
299: }
300:
301: private boolean doesExclude(ModuleDescriptor md,
302: String rootModuleConf, String[] moduleConfs,
303: DependencyDescriptor dd, Artifact artifact,
304: Stack callersStack) {
305: // artifact is excluded if it match any of the exclude pattern for this dependency...
306: if (dd != null) {
307: if (dd.doesExclude(moduleConfs, artifact.getId()
308: .getArtifactId())) {
309: return true;
310: }
311: }
312: if (md.doesExclude(moduleConfs, artifact.getId()
313: .getArtifactId())) {
314: return true;
315: }
316: // ... or if it is excluded by all its callers
317: IvyNode c = node.getData().getNode(md.getModuleRevisionId());
318: if (c != null) {
319: return c.doesCallersExclude(rootModuleConf, artifact,
320: callersStack);
321: } else {
322: return false;
323: }
324: }
325:
326: }
|