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.module.descriptor;
019:
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.Iterator;
025: import java.util.LinkedHashMap;
026: import java.util.LinkedHashSet;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Set;
030: import java.util.regex.Matcher;
031: import java.util.regex.Pattern;
032:
033: import org.apache.ivy.core.module.id.ArtifactId;
034: import org.apache.ivy.core.module.id.ModuleId;
035: import org.apache.ivy.core.module.id.ModuleRevisionId;
036: import org.apache.ivy.plugins.matcher.MatcherHelper;
037: import org.apache.ivy.plugins.namespace.NameSpaceHelper;
038: import org.apache.ivy.plugins.namespace.Namespace;
039: import org.apache.ivy.plugins.namespace.NamespaceTransformer;
040:
041: /**
042: * This class can be used as the default implementation for DependencyDescriptor. It implements
043: * required methods and enables to fill dependency information with the addDependencyConfiguration
044: * method.
045: */
046: public class DefaultDependencyDescriptor implements
047: DependencyDescriptor {
048: private static final Pattern SELF_FALLBACK_PATTERN = Pattern
049: .compile("@(\\(.*\\))?");
050:
051: private static final Pattern THIS_FALLBACK_PATTERN = Pattern
052: .compile("#(\\(.*\\))?");
053:
054: /**
055: * Transforms the given dependency descriptor of the given namespace and return a new dependency
056: * descriptor in the system namespace. <i>Note that exclude rules are not converted in system
057: * namespace, because they aren't transformable (the name space hasn't the ability to convert
058: * regular expressions). However, method doesExclude will work with system artifacts.</i>
059: *
060: * @param dd
061: * @param ns
062: * @return
063: */
064: public static DependencyDescriptor transformInstance(
065: DependencyDescriptor dd, Namespace ns) {
066: NamespaceTransformer t = ns.getToSystemTransformer();
067: if (t.isIdentity()) {
068: return dd;
069: }
070: DefaultDependencyDescriptor newdd = transformInstance(dd, t,
071: false);
072: newdd.namespace = ns;
073: return newdd;
074: }
075:
076: /**
077: * Transforms a dependency descriptor using the given transformer. Note that no namespace info
078: * will be attached to the transformed dependency descriptor, so calling doesExclude is not
079: * recommended (doesExclude only works when namespace is properly set)
080: *
081: * @param dd
082: * @param t
083: * @return
084: */
085: public static DefaultDependencyDescriptor transformInstance(
086: DependencyDescriptor dd, NamespaceTransformer t,
087: boolean fromSystem) {
088: ModuleRevisionId transformParentId = t.transform(dd
089: .getParentRevisionId());
090: ModuleRevisionId transformMrid = t.transform(dd
091: .getDependencyRevisionId());
092: DefaultDependencyDescriptor newdd = new DefaultDependencyDescriptor(
093: null, transformMrid, dd.isForce(), dd.isChanging(), dd
094: .isTransitive());
095: newdd.parentId = transformParentId;
096: String[] moduleConfs = dd.getModuleConfigurations();
097: if (moduleConfs.length == 1 && "*".equals(moduleConfs[0])) {
098: if (dd instanceof DefaultDependencyDescriptor) {
099: DefaultDependencyDescriptor ddd = (DefaultDependencyDescriptor) dd;
100: newdd.confs = new LinkedHashMap(ddd.confs);
101: newdd.setExcludeRules(new LinkedHashMap(ddd
102: .getExcludeRules()));
103: newdd.setIncludeRules(new LinkedHashMap(ddd
104: .getIncludeRules()));
105: newdd.setDependencyArtifacts(new LinkedHashMap(ddd
106: .getDependencyArtifacts()));
107: } else {
108: throw new IllegalArgumentException(
109: "dependency descriptor transformation does not support * module confs "
110: + "with descriptors which aren't DefaultDependencyDescriptor");
111: }
112: } else {
113: for (int i = 0; i < moduleConfs.length; i++) {
114: newdd.confs
115: .put(
116: moduleConfs[i],
117: new ArrayList(
118: Arrays
119: .asList(dd
120: .getDependencyConfigurations(moduleConfs[i]))));
121: newdd.getExcludeRules().put(
122: moduleConfs[i],
123: new ArrayList(Arrays.asList(dd
124: .getExcludeRules(moduleConfs[i]))));
125: newdd.getIncludeRules().put(
126: moduleConfs[i],
127: new ArrayList(Arrays.asList(dd
128: .getIncludeRules(moduleConfs[i]))));
129: newdd
130: .getDependencyArtifacts()
131: .put(
132: moduleConfs[i],
133: new ArrayList(
134: Arrays
135: .asList(dd
136: .getDependencyArtifacts(moduleConfs[i]))));
137: }
138: }
139: if (fromSystem) {
140: newdd.asSystem = dd;
141: }
142: return newdd;
143: }
144:
145: private ModuleRevisionId revId;
146:
147: private Map confs = new LinkedHashMap();
148:
149: // Map (String masterConf -> Collection(DependencyArtifactDescriptor))
150: private Map dependencyArtifacts; // initialized on demand only for memory consumption reason
151:
152: // Map (String masterConf -> Collection(IncludeRule))
153: private Map includeRules; // initialized on demand only for memory consumption reason
154:
155: // Map (String masterConf -> Collection(ExcludeRule))
156: private Map excludeRules; // initialized on demand only for memory consumption reason
157:
158: /**
159: * Used to indicate that this revision must be used in case of conflicts, independently of
160: * conflicts manager
161: */
162: private boolean isForce;
163:
164: /**
165: * Used to indicate that the dependency is a changing one, i.e. that ivy should not rely on the
166: * version to know if it can trust artifacts in cache
167: */
168: private boolean isChanging;
169:
170: private ModuleRevisionId parentId;
171:
172: private boolean isTransitive = true;
173:
174: /**
175: * This namespace should be used to check
176: */
177: private Namespace namespace = null;
178:
179: private final ModuleDescriptor md;
180:
181: private DependencyDescriptor asSystem = this ;
182:
183: public DefaultDependencyDescriptor(DependencyDescriptor dd,
184: String revision) {
185: md = null;
186: parentId = dd.getParentRevisionId();
187: revId = ModuleRevisionId.newInstance(dd
188: .getDependencyRevisionId(), revision);
189: isForce = dd.isForce();
190: isChanging = dd.isChanging();
191: isTransitive = dd.isTransitive();
192: String[] moduleConfs = dd.getModuleConfigurations();
193: Map excludeRules = getExcludeRules();
194: Map includeRules = getIncludeRules();
195: for (int i = 0; i < moduleConfs.length; i++) {
196: confs.put(moduleConfs[i], new ArrayList(Arrays.asList(dd
197: .getDependencyConfigurations(moduleConfs[i]))));
198: excludeRules.put(moduleConfs[i], new ArrayList(Arrays
199: .asList(dd.getExcludeRules(moduleConfs[i]))));
200: includeRules.put(moduleConfs[i], new ArrayList(Arrays
201: .asList(dd.getIncludeRules(moduleConfs[i]))));
202: }
203: }
204:
205: public DefaultDependencyDescriptor(ModuleDescriptor md,
206: ModuleRevisionId mrid, boolean force, boolean changing,
207: boolean transitive) {
208: this .md = md;
209: revId = mrid;
210: isForce = force;
211: isChanging = changing;
212: isTransitive = transitive;
213: }
214:
215: public DefaultDependencyDescriptor(ModuleRevisionId mrid,
216: boolean force) {
217: this (mrid, force, false);
218: }
219:
220: public DefaultDependencyDescriptor(ModuleRevisionId mrid,
221: boolean force, boolean changing) {
222: md = null;
223: revId = mrid;
224: isForce = force;
225: isChanging = changing;
226: }
227:
228: public ModuleId getDependencyId() {
229: return getDependencyRevisionId().getModuleId();
230: }
231:
232: public ModuleRevisionId getDependencyRevisionId() {
233: return revId;
234: }
235:
236: public String[] getModuleConfigurations() {
237: return (String[]) confs.keySet().toArray(
238: new String[confs.keySet().size()]);
239: }
240:
241: public String[] getDependencyConfigurations(
242: String moduleConfiguration) {
243: return getDependencyConfigurations(moduleConfiguration,
244: moduleConfiguration);
245: }
246:
247: /**
248: * Return the dependency configurations mapped to the given moduleConfiguration, actually
249: * resolved because of the given requestedConfiguration Usually requestedConfiguration and
250: * moduleConfiguration are the same, except when a conf extends another, then the
251: * moduleConfiguration is the configuration currently resolved (the extended one), and
252: * requestedConfiguration is the one actually requested initially (the extending one). Both
253: * moduleConfiguration and requestedConfiguration are configurations of the caller, the array
254: * returned is composed of the required configurations of the dependency described by this
255: * descriptor.
256: */
257: public String[] getDependencyConfigurations(
258: String moduleConfiguration, String requestedConfiguration) {
259: List confsList = (List) confs.get(moduleConfiguration);
260: if (confsList == null) {
261: // there is no mapping defined for this configuration, add the 'other' mappings.
262: confsList = (List) confs.get("%");
263: }
264: List defConfs = (List) confs.get("*");
265: Collection ret = new LinkedHashSet();
266: if (confsList != null) {
267: ret.addAll(confsList);
268: }
269: if (defConfs != null) {
270: ret.addAll(defConfs);
271: }
272:
273: Collection replacedRet = new LinkedHashSet();
274: for (Iterator iter = ret.iterator(); iter.hasNext();) {
275: String c = (String) iter.next();
276: String replacedConf = replaceSelfFallbackPattern(c,
277: moduleConfiguration);
278: if (replacedConf == null) {
279: replacedConf = replaceThisFallbackPattern(c,
280: requestedConfiguration);
281: }
282: if (replacedConf != null) {
283: c = replacedConf;
284: }
285: replacedRet.add(c);
286: }
287: ret = replacedRet;
288: if (ret.remove("*")) {
289: StringBuffer r = new StringBuffer("*");
290: // merge excluded configurations as one conf like *!A!B
291: for (Iterator iter = ret.iterator(); iter.hasNext();) {
292: String c = (String) iter.next();
293: if (c.startsWith("!")) {
294: r.append(c);
295: }
296: }
297: return new String[] { r.toString() };
298: }
299: return (String[]) ret.toArray(new String[ret.size()]);
300: }
301:
302: protected static String replaceSelfFallbackPattern(
303: final String conf, final String moduleConfiguration) {
304: return replaceFallbackConfigurationPattern(
305: SELF_FALLBACK_PATTERN, conf, moduleConfiguration);
306: }
307:
308: protected static String replaceThisFallbackPattern(
309: final String conf, final String requestedConfiguration) {
310: return replaceFallbackConfigurationPattern(
311: THIS_FALLBACK_PATTERN, conf, requestedConfiguration);
312: }
313:
314: /**
315: * Replaces fallback patterns with correct values if fallback pattern exists.
316: *
317: * @param pattern
318: * pattern to look for
319: * @param conf
320: * configuration mapping from dependency element
321: * @param moduleConfiguration
322: * module's configuration to use for replacement
323: * @return Replaced string if pattern matched. Otherwise null.
324: */
325: protected static String replaceFallbackConfigurationPattern(
326: final Pattern pattern, final String conf,
327: final String moduleConfiguration) {
328: Matcher matcher = pattern.matcher(conf);
329: if (matcher.matches()) {
330: if (matcher.group(1) != null) {
331: return moduleConfiguration + matcher.group(1);
332: } else {
333: return moduleConfiguration;
334: }
335: }
336: return null;
337: }
338:
339: public String[] getDependencyConfigurations(
340: String[] moduleConfigurations) {
341: Set confs = new LinkedHashSet();
342: for (int i = 0; i < moduleConfigurations.length; i++) {
343: confs
344: .addAll(Arrays
345: .asList(getDependencyConfigurations(moduleConfigurations[i])));
346: }
347: if (confs.contains("*")) {
348: return new String[] { "*" };
349: }
350: return (String[]) confs.toArray(new String[confs.size()]);
351: }
352:
353: public DependencyArtifactDescriptor[] getDependencyArtifacts(
354: String moduleConfiguration) {
355: Collection artifacts = getCollectionForConfiguration(
356: moduleConfiguration, dependencyArtifacts);
357: return (DependencyArtifactDescriptor[]) artifacts
358: .toArray(new DependencyArtifactDescriptor[artifacts
359: .size()]);
360: }
361:
362: public IncludeRule[] getIncludeRules(String moduleConfiguration) {
363: Collection rules = getCollectionForConfiguration(
364: moduleConfiguration, includeRules);
365: return (IncludeRule[]) rules.toArray(new IncludeRule[rules
366: .size()]);
367: }
368:
369: public ExcludeRule[] getExcludeRules(String moduleConfiguration) {
370: Collection rules = getCollectionForConfiguration(
371: moduleConfiguration, excludeRules);
372: return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules
373: .size()]);
374: }
375:
376: private Set getCollectionForConfiguration(
377: String moduleConfiguration, Map collectionMap) {
378: if (collectionMap == null || collectionMap.isEmpty()) {
379: return Collections.EMPTY_SET;
380: }
381: Collection artifacts = (Collection) collectionMap
382: .get(moduleConfiguration);
383: Collection defArtifacts = (Collection) collectionMap.get("*");
384: Set ret = new LinkedHashSet();
385: if (artifacts != null) {
386: ret.addAll(artifacts);
387: }
388: if (defArtifacts != null) {
389: ret.addAll(defArtifacts);
390: }
391: return ret;
392: }
393:
394: public DependencyArtifactDescriptor[] getDependencyArtifacts(
395: String[] moduleConfigurations) {
396: Set artifacts = new LinkedHashSet();
397: for (int i = 0; i < moduleConfigurations.length; i++) {
398: artifacts
399: .addAll(Arrays
400: .asList(getDependencyArtifacts(moduleConfigurations[i])));
401: }
402: return (DependencyArtifactDescriptor[]) artifacts
403: .toArray(new DependencyArtifactDescriptor[artifacts
404: .size()]);
405: }
406:
407: public IncludeRule[] getIncludeRules(String[] moduleConfigurations) {
408: Set rules = new LinkedHashSet();
409: for (int i = 0; i < moduleConfigurations.length; i++) {
410: rules.addAll(Arrays
411: .asList(getIncludeRules(moduleConfigurations[i])));
412: }
413: return (IncludeRule[]) rules.toArray(new IncludeRule[rules
414: .size()]);
415: }
416:
417: public ExcludeRule[] getExcludeRules(String[] moduleConfigurations) {
418: Set rules = new LinkedHashSet();
419: for (int i = 0; i < moduleConfigurations.length; i++) {
420: rules.addAll(Arrays
421: .asList(getExcludeRules(moduleConfigurations[i])));
422: }
423: return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules
424: .size()]);
425: }
426:
427: public DependencyArtifactDescriptor[] getAllDependencyArtifacts() {
428: if (dependencyArtifacts == null) {
429: return new DependencyArtifactDescriptor[0];
430: }
431: Set ret = mergeAll(dependencyArtifacts);
432: return (DependencyArtifactDescriptor[]) ret
433: .toArray(new DependencyArtifactDescriptor[ret.size()]);
434: }
435:
436: public IncludeRule[] getAllIncludeRules() {
437: if (includeRules == null) {
438: return new IncludeRule[0];
439: }
440: Set ret = mergeAll(includeRules);
441: return (IncludeRule[]) ret.toArray(new IncludeRule[ret.size()]);
442: }
443:
444: public ExcludeRule[] getAllExcludeRules() {
445: if (excludeRules == null) {
446: return new ExcludeRule[0];
447: }
448: Set ret = mergeAll(excludeRules);
449: return (ExcludeRule[]) ret.toArray(new ExcludeRule[ret.size()]);
450: }
451:
452: private Set mergeAll(Map artifactsMap) {
453: Set ret = new LinkedHashSet();
454: for (Iterator it = artifactsMap.values().iterator(); it
455: .hasNext();) {
456: Collection artifacts = (Collection) it.next();
457: ret.addAll(artifacts);
458: }
459: return ret;
460: }
461:
462: public void addDependencyConfiguration(String masterConf,
463: String depConf) {
464: if ((md != null) && !"*".equals(masterConf)
465: && !"%".equals(masterConf)) {
466: Configuration config = md.getConfiguration(masterConf);
467: if (config == null) {
468: throw new IllegalArgumentException("Configuration '"
469: + masterConf + "' does not exist in module "
470: + md);
471: }
472: }
473:
474: List confsList = (List) confs.get(masterConf);
475: if (confsList == null) {
476: confsList = new ArrayList();
477: confs.put(masterConf, confsList);
478: }
479: if (!confsList.contains(depConf)) {
480: confsList.add(depConf);
481: }
482: }
483:
484: public void addDependencyArtifact(String masterConf,
485: DependencyArtifactDescriptor dad) {
486: addObjectToConfiguration(masterConf, dad,
487: getDependencyArtifacts());
488: }
489:
490: public void addIncludeRule(String masterConf, IncludeRule rule) {
491: addObjectToConfiguration(masterConf, rule, getIncludeRules());
492: }
493:
494: public void addExcludeRule(String masterConf, ExcludeRule rule) {
495: addObjectToConfiguration(masterConf, rule, getExcludeRules());
496: }
497:
498: private void addObjectToConfiguration(String callerConf,
499: Object toAdd, Map confsMap) {
500: Collection col = (Collection) confsMap.get(callerConf);
501: if (col == null) {
502: col = new ArrayList();
503: confsMap.put(callerConf, col);
504: }
505: col.add(toAdd);
506: }
507:
508: /**
509: * only works when namespace is properly set. The behaviour is not specified if namespace is not
510: * set
511: */
512: public boolean doesExclude(String[] moduleConfigurations,
513: ArtifactId artifactId) {
514: if (namespace != null) {
515: artifactId = NameSpaceHelper.transform(artifactId,
516: namespace.getFromSystemTransformer());
517: }
518: ExcludeRule[] rules = getExcludeRules(moduleConfigurations);
519: for (int i = 0; i < rules.length; i++) {
520: if (MatcherHelper.matches(rules[i].getMatcher(), rules[i]
521: .getId(), artifactId)) {
522: return true;
523: }
524: }
525: return false;
526: }
527:
528: /**
529: * Returns true if this descriptor contains any exclusion rule
530: *
531: * @return
532: */
533: public boolean canExclude() {
534: return excludeRules != null && !excludeRules.isEmpty();
535: }
536:
537: public String toString() {
538: return "dependency: " + revId + " " + confs;
539: }
540:
541: public boolean isForce() {
542: return isForce;
543: }
544:
545: public ModuleRevisionId getParentRevisionId() {
546: return md != null ? md.getResolvedModuleRevisionId() : parentId;
547: }
548:
549: public boolean isChanging() {
550: return isChanging;
551: }
552:
553: public boolean isTransitive() {
554: return isTransitive;
555: }
556:
557: public Namespace getNamespace() {
558: return namespace;
559: }
560:
561: public String getAttribute(String attName) {
562: return revId.getAttribute(attName);
563: }
564:
565: public Map getAttributes() {
566: return revId.getAttributes();
567: }
568:
569: public String getExtraAttribute(String attName) {
570: return revId.getExtraAttribute(attName);
571: }
572:
573: public Map getExtraAttributes() {
574: return revId.getExtraAttributes();
575: }
576:
577: public Map getQualifiedExtraAttributes() {
578: return revId.getQualifiedExtraAttributes();
579: }
580:
581: public String getStandardAttribute(String attName) {
582: return revId.getStandardAttribute(attName);
583: }
584:
585: public Map getStandardAttributes() {
586: return revId.getStandardAttributes();
587: }
588:
589: public DependencyDescriptor asSystem() {
590: return asSystem;
591: }
592:
593: private void setDependencyArtifacts(Map dependencyArtifacts) {
594: this .dependencyArtifacts = dependencyArtifacts;
595: }
596:
597: private Map getDependencyArtifacts() {
598: if (dependencyArtifacts == null) {
599: dependencyArtifacts = new LinkedHashMap();
600: }
601: return dependencyArtifacts;
602: }
603:
604: private void setIncludeRules(Map includeRules) {
605: this .includeRules = includeRules;
606: }
607:
608: private Map getIncludeRules() {
609: if (includeRules == null) {
610: includeRules = new LinkedHashMap();
611: }
612: return includeRules;
613: }
614:
615: private void setExcludeRules(Map excludeRules) {
616: this .excludeRules = excludeRules;
617: }
618:
619: private Map getExcludeRules() {
620: if (excludeRules == null) {
621: excludeRules = new LinkedHashMap();
622: }
623: return excludeRules;
624: }
625:
626: }
|