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.plugins.parser.xml;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.MalformedURLException;
024: import java.net.URL;
025: import java.text.ParseException;
026: import java.util.Arrays;
027: import java.util.Collections;
028: import java.util.List;
029: import java.util.Map;
030:
031: import javax.xml.parsers.ParserConfigurationException;
032:
033: import org.apache.ivy.Ivy;
034: import org.apache.ivy.core.IvyContext;
035: import org.apache.ivy.core.module.descriptor.Configuration;
036: import org.apache.ivy.core.module.descriptor.ConfigurationAware;
037: import org.apache.ivy.core.module.descriptor.DefaultArtifact;
038: import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
039: import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
040: import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
041: import org.apache.ivy.core.module.descriptor.DefaultIncludeRule;
042: import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
043: import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
044: import org.apache.ivy.core.module.descriptor.ExcludeRule;
045: import org.apache.ivy.core.module.descriptor.IncludeRule;
046: import org.apache.ivy.core.module.descriptor.License;
047: import org.apache.ivy.core.module.descriptor.MDArtifact;
048: import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
049: import org.apache.ivy.core.module.id.ArtifactId;
050: import org.apache.ivy.core.module.id.ModuleId;
051: import org.apache.ivy.core.module.id.ModuleRevisionId;
052: import org.apache.ivy.plugins.conflict.ConflictManager;
053: import org.apache.ivy.plugins.conflict.FixedConflictManager;
054: import org.apache.ivy.plugins.matcher.PatternMatcher;
055: import org.apache.ivy.plugins.namespace.Namespace;
056: import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParser;
057: import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
058: import org.apache.ivy.plugins.parser.ParserSettings;
059: import org.apache.ivy.plugins.repository.Resource;
060: import org.apache.ivy.plugins.repository.url.URLResource;
061: import org.apache.ivy.util.Message;
062: import org.apache.ivy.util.XMLHelper;
063: import org.apache.ivy.util.extendable.ExtendableItemHelper;
064: import org.xml.sax.Attributes;
065: import org.xml.sax.SAXException;
066:
067: /**
068: * Parses an xml ivy file and output a ModuleDescriptor. For dependency and performance reasons, it
069: * uses only the SAX API, which makes the parsing code harder to understand.
070: */
071: public final class XmlModuleDescriptorParser extends
072: AbstractModuleDescriptorParser {
073: static final String[] DEPENDENCY_REGULAR_ATTRIBUTES = new String[] {
074: "org", "name", "branch", "rev", "force", "transitive",
075: "changing", "conf" };
076:
077: private static final XmlModuleDescriptorParser INSTANCE = new XmlModuleDescriptorParser();
078:
079: public static XmlModuleDescriptorParser getInstance() {
080: return INSTANCE;
081: }
082:
083: private XmlModuleDescriptorParser() {
084:
085: }
086:
087: /**
088: * @param ivy
089: * @param xmlURL
090: * the url pointing to the file to parse
091: * @param res
092: * the real resource to parse, used for log only
093: * @param validate
094: * @return
095: * @throws ParseException
096: * @throws IOException
097: */
098: public ModuleDescriptor parseDescriptor(ParserSettings ivySettings,
099: URL xmlURL, Resource res, boolean validate)
100: throws ParseException, IOException {
101: Parser parser = new Parser(this , ivySettings, validate, xmlURL);
102: parser.parse(res, validate);
103: return parser.getModuleDescriptor();
104: }
105:
106: /** Used for test purpose */
107: ModuleDescriptor parseDescriptor(ParserSettings ivySettings,
108: InputStream descriptor, Resource res, boolean validate)
109: throws ParseException, IOException {
110: Parser parser = new Parser(this , ivySettings, validate, null);
111: parser.parse(descriptor, res, validate);
112: return parser.getModuleDescriptor();
113: }
114:
115: public boolean accept(Resource res) {
116: return true; // this the default parser, it thus accepts all resources
117: }
118:
119: public void toIvyFile(InputStream is, Resource res, File destFile,
120: ModuleDescriptor md) throws IOException, ParseException {
121: try {
122: Namespace ns = null;
123: if (md instanceof DefaultModuleDescriptor) {
124: DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
125: ns = dmd.getNamespace();
126: }
127: XmlModuleDescriptorUpdater.update(IvyContext.getContext()
128: .getSettings(), is, res, destFile,
129: Collections.EMPTY_MAP, md.getStatus(), md
130: .getResolvedModuleRevisionId()
131: .getRevision(), md
132: .getResolvedPublicationDate(), ns, true,
133: null);
134: } catch (SAXException e) {
135: ParseException ex = new ParseException(
136: "exception occured while parsing " + res, 0);
137: ex.initCause(e);
138: throw ex;
139: } finally {
140: if (is != null) {
141: is.close();
142: }
143: }
144: }
145:
146: private static class Parser extends AbstractParser {
147:
148: private static final List ALLOWED_VERSIONS = Arrays
149: .asList(new String[] { "1.0", "1.1", "1.2", "1.3",
150: "1.4", "2.0" });
151:
152: private DefaultDependencyDescriptor dd;
153:
154: private ConfigurationAware confAware;
155:
156: private MDArtifact artifact;
157:
158: private String conf;
159:
160: private boolean validate = true;
161:
162: private ParserSettings ivy;
163:
164: private boolean artifactsDeclared = false;
165:
166: private PatternMatcher defaultMatcher;
167:
168: private static final int NONE = 0;
169:
170: private static final int INFO = 1;
171:
172: private static final int CONF = 2;
173:
174: private static final int PUB = 3;
175:
176: private static final int DEP = 4;
177:
178: private static final int DEP_ARTIFACT = 5;
179:
180: private static final int ARTIFACT_INCLUDE = 6;
181:
182: private static final int ARTIFACT_EXCLUDE = 7;
183:
184: private static final int CONFLICT = 8;
185:
186: private static final int EXCLUDE = 9;
187:
188: private static final int DEPS = 10;
189:
190: private static final int DESCRIPTION = 11;
191:
192: private int state = NONE;
193:
194: private final URL xmlURL;
195:
196: private StringBuffer buffer;
197:
198: public Parser(ModuleDescriptorParser parser,
199: ParserSettings ivySettings, boolean validate, URL xmlURL) {
200: super (parser);
201: ivy = ivySettings;
202: this .validate = validate;
203: this .xmlURL = xmlURL;
204: }
205:
206: private void parse(Resource res, boolean validate)
207: throws ParseException, IOException {
208: try {
209: setResource(res);
210: URL schemaURL = validate ? getClass().getResource(
211: "ivy.xsd") : null;
212: XMLHelper.parse(xmlURL, schemaURL, this );
213: checkConfigurations();
214: replaceConfigurationWildcards();
215: md.setModuleArtifact(DefaultArtifact.newIvyArtifact(md
216: .getResolvedModuleRevisionId(), md
217: .getPublicationDate()));
218: if (!artifactsDeclared) {
219: String[] confs = md.getConfigurationsNames();
220: for (int i = 0; i < confs.length; i++) {
221: md.addArtifact(confs[i], new MDArtifact(md, md
222: .getModuleRevisionId().getName(),
223: "jar", "jar"));
224: }
225: }
226: md.check();
227: } catch (ParserConfigurationException ex) {
228: IllegalStateException ise = new IllegalStateException(
229: ex.getMessage() + " in " + xmlURL);
230: ise.initCause(ex);
231: throw ise;
232: } catch (Exception ex) {
233: checkErrors();
234: ParseException pe = new ParseException(ex.getMessage()
235: + " in " + xmlURL, 0);
236: pe.initCause(ex);
237: throw pe;
238: }
239: }
240:
241: private void parse(InputStream descriptor, Resource res,
242: boolean validate) throws ParseException, IOException {
243: try {
244: setResource(res);
245: URL schemaURL = validate ? getClass().getResource(
246: "ivy.xsd") : null;
247: XMLHelper.parse(descriptor, schemaURL, this , null);
248: checkConfigurations();
249: replaceConfigurationWildcards();
250: if (!artifactsDeclared) {
251: String[] confs = md.getConfigurationsNames();
252: for (int i = 0; i < confs.length; i++) {
253: md.addArtifact(confs[i], new MDArtifact(md, md
254: .getModuleRevisionId().getName(),
255: "jar", "jar"));
256: }
257: }
258: md.check();
259: } catch (ParserConfigurationException ex) {
260: IllegalStateException ise = new IllegalStateException(
261: ex.getMessage());
262: ise.initCause(ex);
263: throw ise;
264: } catch (Exception ex) {
265: checkErrors();
266: ParseException pe = new ParseException(ex.getMessage(),
267: 0);
268: pe.initCause(ex);
269: throw pe;
270: }
271: }
272:
273: public void startElement(String uri, String localName,
274: String qName, Attributes attributes)
275: throws SAXException {
276: try {
277: if ("ivy-module".equals(qName)) {
278: ivyModuleStarted(attributes);
279: } else if ("info".equals(qName)) {
280: infoStarted(attributes);
281: } else if (state == INFO && "license".equals(qName)) {
282: md.addLicense(new License(ivy.substitute(attributes
283: .getValue("name")), ivy
284: .substitute(attributes.getValue("url"))));
285: } else if (state == INFO && "description".equals(qName)) {
286: md.setHomePage(ivy.substitute(attributes
287: .getValue("homepage")));
288: state = DESCRIPTION;
289: } else if (state == INFO) {
290: buffer = new StringBuffer();
291: } else if ("configurations".equals(qName)) {
292: configurationStarted(attributes);
293: } else if ("publications".equals(qName)) {
294: state = PUB;
295: artifactsDeclared = true;
296: checkConfigurations();
297: } else if ("dependencies".equals(qName)) {
298: dependenciesStarted(attributes);
299: } else if ("conflicts".equals(qName)) {
300: state = CONFLICT;
301: checkConfigurations();
302: } else if ("artifact".equals(qName)) {
303: artifactStarted(qName, attributes);
304: } else if ("include".equals(qName) && state == DEP) {
305: addIncludeRule(qName, attributes);
306: } else if ("exclude".equals(qName) && state == DEP) {
307: addExcludeRule(qName, attributes);
308: } else if ("exclude".equals(qName) && state == DEPS) {
309: state = EXCLUDE;
310: parseRule(qName, attributes);
311: md.addExcludeRule((ExcludeRule) confAware);
312: } else if ("dependency".equals(qName)) {
313: dependencyStarted(attributes);
314: } else if ("conf".equals(qName)) {
315: confStarted(attributes);
316: } else if ("mapped".equals(qName)) {
317: dd.addDependencyConfiguration(conf, ivy
318: .substitute(attributes.getValue("name")));
319: } else if ("manager".equals(qName) && state == CONFLICT) {
320: managerStarted(attributes);
321: } else if ("include".equals(qName) && state == CONF) {
322: includeConfStarted(attributes);
323: } else if (validate && state != INFO) {
324: addError("unknwon tag " + qName);
325: }
326: } catch (Exception ex) {
327: if (ex instanceof SAXException) {
328: throw (SAXException) ex;
329: }
330: throw new SAXException(
331: "problem occured while parsing ivy file. message: "
332: + ex.getMessage(), ex);
333: }
334: }
335:
336: private void managerStarted(Attributes attributes) {
337: String org = ivy.substitute(attributes.getValue("org"));
338: org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
339: String mod = ivy.substitute(attributes.getValue("module"));
340: mod = mod == null ? PatternMatcher.ANY_EXPRESSION : mod;
341: ConflictManager cm;
342: String name = ivy.substitute(attributes.getValue("name"));
343: String rev = ivy.substitute(attributes.getValue("rev"));
344: if (rev != null) {
345: String[] revs = rev.split(",");
346: for (int i = 0; i < revs.length; i++) {
347: revs[i] = revs[i].trim();
348: }
349: cm = new FixedConflictManager(revs);
350: } else if (name != null) {
351: cm = ivy.getConflictManager(name);
352: if (cm == null) {
353: addError("unknown conflict manager: " + name);
354: return;
355: }
356: } else {
357: addError("bad conflict manager: no name nor rev");
358: return;
359: }
360: String matcherName = ivy.substitute(attributes
361: .getValue("matcher"));
362: PatternMatcher matcher = matcherName == null ? defaultMatcher
363: : ivy.getMatcher(matcherName);
364: if (matcher == null) {
365: addError("unknown matcher: " + matcherName);
366: return;
367: }
368: md.addConflictManager(new ModuleId(org, mod), matcher, cm);
369: }
370:
371: private void includeConfStarted(Attributes attributes)
372: throws SAXException, IOException,
373: ParserConfigurationException, ParseException {
374: URL url = ivy.getRelativeUrlResolver().getURL(xmlURL,
375: ivy.substitute(attributes.getValue("file")),
376: ivy.substitute(attributes.getValue("url")));
377:
378: if (url == null) {
379: throw new SAXException(
380: "include tag must have a file or an url attribute");
381: }
382:
383: // create a new temporary parser to read the configurations from
384: // the specified file.
385: Parser parser = new Parser(getModuleDescriptorParser(),
386: ivy, false, url);
387: parser.md = new DefaultModuleDescriptor(
388: getModuleDescriptorParser(), new URLResource(url));
389: XMLHelper.parse(url, null, parser);
390:
391: // add the configurations from this temporary parser to this module descriptor
392: Configuration[] configs = parser.getModuleDescriptor()
393: .getConfigurations();
394: for (int i = 0; i < configs.length; i++) {
395: md.addConfiguration(configs[i]);
396: }
397: if (parser.getDefaultConfMapping() != null) {
398: Message
399: .debug("setting default conf from imported configurations file: "
400: + parser.getDefaultConfMapping());
401: setDefaultConfMapping(parser.getDefaultConfMapping());
402: }
403: if (parser.md.isMappingOverride()) {
404: Message
405: .debug("enabling mapping-override from imported configurations"
406: + " file");
407: md.setMappingOverride(true);
408: }
409: }
410:
411: private void confStarted(Attributes attributes) {
412: String conf = ivy.substitute(attributes.getValue("name"));
413: switch (state) {
414: case CONF:
415: String visibility = ivy.substitute(attributes
416: .getValue("visibility"));
417: String ext = ivy.substitute(attributes
418: .getValue("extends"));
419: String transitiveValue = attributes
420: .getValue("transitive");
421: boolean transitive = (transitiveValue == null) ? true
422: : Boolean.valueOf(
423: attributes.getValue("transitive"))
424: .booleanValue();
425: String deprecated = attributes.getValue("deprecated");
426: Configuration configuration = new Configuration(
427: conf,
428: Configuration.Visibility
429: .getVisibility(visibility == null ? "public"
430: : visibility), ivy
431: .substitute(attributes
432: .getValue("description")),
433: ext == null ? null : ext.split(","),
434: transitive, deprecated);
435: ExtendableItemHelper.fillExtraAttributes(configuration,
436: attributes, new String[] { "name",
437: "visibility", "extends", "transitive",
438: "description", "deprecated" });
439: md.addConfiguration(configuration);
440: break;
441: case PUB:
442: if ("*".equals(conf)) {
443: String[] confs = md.getConfigurationsNames();
444: for (int i = 0; i < confs.length; i++) {
445: artifact.addConfiguration(confs[i]);
446: md.addArtifact(confs[i], artifact);
447: }
448: } else {
449: artifact.addConfiguration(conf);
450: md.addArtifact(conf, artifact);
451: }
452: break;
453: case DEP:
454: this .conf = conf;
455: String mappeds = ivy.substitute(attributes
456: .getValue("mapped"));
457: if (mappeds != null) {
458: String[] mapped = mappeds.split(",");
459: for (int i = 0; i < mapped.length; i++) {
460: dd.addDependencyConfiguration(conf, mapped[i]
461: .trim());
462: }
463: }
464: break;
465: case DEP_ARTIFACT:
466: case ARTIFACT_INCLUDE:
467: case ARTIFACT_EXCLUDE:
468: addConfiguration(conf);
469: break;
470: default:
471: if (validate) {
472: addError("conf tag found in invalid tag: " + state);
473: }
474: break;
475: }
476: }
477:
478: private void dependencyStarted(Attributes attributes) {
479: state = DEP;
480: String org = ivy.substitute(attributes.getValue("org"));
481: if (org == null) {
482: org = md.getModuleRevisionId().getOrganisation();
483: }
484: boolean force = Boolean.valueOf(
485: ivy.substitute(attributes.getValue("force")))
486: .booleanValue();
487: boolean changing = Boolean.valueOf(
488: ivy.substitute(attributes.getValue("changing")))
489: .booleanValue();
490:
491: String transitiveValue = ivy.substitute(attributes
492: .getValue("transitive"));
493: boolean transitive = (transitiveValue == null) ? true
494: : Boolean
495: .valueOf(attributes.getValue("transitive"))
496: .booleanValue();
497:
498: String name = ivy.substitute(attributes.getValue("name"));
499: String branch = ivy.substitute(attributes
500: .getValue("branch"));
501: String rev = ivy.substitute(attributes.getValue("rev"));
502: dd = new DefaultDependencyDescriptor(md, ModuleRevisionId
503: .newInstance(org, name, branch, rev,
504: ExtendableItemHelper.getExtraAttributes(
505: attributes,
506: DEPENDENCY_REGULAR_ATTRIBUTES)),
507: force, changing, transitive);
508: md.addDependency(dd);
509: String confs = ivy.substitute(attributes.getValue("conf"));
510: if (confs != null && confs.length() > 0) {
511: parseDepsConfs(confs, dd);
512: }
513: }
514:
515: private void artifactStarted(String qName, Attributes attributes)
516: throws MalformedURLException {
517: if (state == PUB) {
518: // this is a published artifact
519: String artName = ivy.substitute(attributes
520: .getValue("name"));
521: artName = artName == null ? md.getModuleRevisionId()
522: .getName() : artName;
523: String type = ivy.substitute(attributes
524: .getValue("type"));
525: type = type == null ? "jar" : type;
526: String ext = ivy.substitute(attributes.getValue("ext"));
527: ext = ext != null ? ext : type;
528: String url = ivy.substitute(attributes.getValue("url"));
529: artifact = new MDArtifact(md, artName, type, ext,
530: url == null ? null : new URL(url),
531: ExtendableItemHelper.getExtraAttributes(
532: attributes, new String[] { "ext",
533: "type", "name", "conf" }));
534: String confs = ivy.substitute(attributes
535: .getValue("conf"));
536: // only add confs if they are specified. if they aren't, endElement will
537: // handle this
538: // only if there are no conf defined in sub elements
539: if (confs != null && confs.length() > 0) {
540: String[] conf;
541: if ("*".equals(confs)) {
542: conf = md.getConfigurationsNames();
543: } else {
544: conf = confs.split(",");
545: }
546: for (int i = 0; i < conf.length; i++) {
547: artifact.addConfiguration(conf[i].trim());
548: md.addArtifact(conf[i].trim(), artifact);
549: }
550: }
551: } else if (state == DEP) {
552: // this is an artifact asked for a particular dependency
553: addDependencyArtifacts(qName, attributes);
554: } else if (validate) {
555: addError("artifact tag found in invalid tag: " + state);
556: }
557: }
558:
559: private void dependenciesStarted(Attributes attributes) {
560: state = DEPS;
561: String defaultConf = ivy.substitute(attributes
562: .getValue("defaultconf"));
563: if (defaultConf != null) {
564: setDefaultConf(defaultConf);
565: }
566: defaultConf = ivy.substitute(attributes
567: .getValue("defaultconfmapping"));
568: if (defaultConf != null) {
569: setDefaultConfMapping(defaultConf);
570: }
571: String confMappingOverride = ivy.substitute(attributes
572: .getValue("confmappingoverride"));
573: if (confMappingOverride != null) {
574: md.setMappingOverride(Boolean.valueOf(
575: confMappingOverride).booleanValue());
576: }
577: checkConfigurations();
578: }
579:
580: private void configurationStarted(Attributes attributes) {
581: state = CONF;
582: setDefaultConfMapping(ivy.substitute(attributes
583: .getValue("defaultconfmapping")));
584: md.setMappingOverride(Boolean.valueOf(
585: ivy.substitute(attributes
586: .getValue("confmappingoverride")))
587: .booleanValue());
588: }
589:
590: private void infoStarted(Attributes attributes) {
591: state = INFO;
592: String org = ivy.substitute(attributes
593: .getValue("organisation"));
594: String module = ivy.substitute(attributes
595: .getValue("module"));
596: String revision = ivy.substitute(attributes
597: .getValue("revision"));
598: String branch = ivy.substitute(attributes
599: .getValue("branch"));
600: md.setModuleRevisionId(ModuleRevisionId.newInstance(org,
601: module, branch, revision, ExtendableItemHelper
602: .getExtraAttributes(attributes,
603: new String[] { "organisation",
604: "module", "revision",
605: "status", "publication",
606: "branch", "namespace",
607: "default", "resolver" })));
608:
609: String namespace = ivy.substitute(attributes
610: .getValue("namespace"));
611: if (namespace != null) {
612: Namespace ns = ivy.getNamespace(namespace);
613: if (ns == null) {
614: Message.warn("namespace not found for "
615: + md.getModuleRevisionId() + ": "
616: + namespace);
617: } else {
618: md.setNamespace(ns);
619: }
620: }
621:
622: String status = ivy.substitute(attributes
623: .getValue("status"));
624: md.setStatus(status == null ? ivy.getStatusManager()
625: .getDefaultStatus() : status);
626: md.setDefault(Boolean.valueOf(
627: ivy.substitute(attributes.getValue("default")))
628: .booleanValue());
629: String pubDate = ivy.substitute(attributes
630: .getValue("publication"));
631: if (pubDate != null && pubDate.length() > 0) {
632: try {
633: md.setPublicationDate(Ivy.DATE_FORMAT
634: .parse(pubDate));
635: } catch (ParseException e) {
636: addError("invalid publication date format: "
637: + pubDate);
638: md.setPublicationDate(getDefaultPubDate());
639: }
640: } else {
641: md.setPublicationDate(getDefaultPubDate());
642: }
643: }
644:
645: private void ivyModuleStarted(Attributes attributes)
646: throws SAXException {
647: String version = attributes.getValue("version");
648: int versionIndex = ALLOWED_VERSIONS.indexOf(version);
649: if (versionIndex == -1) {
650: addError("invalid version " + version);
651: throw new SAXException("invalid version " + version);
652: }
653: if (versionIndex >= ALLOWED_VERSIONS.indexOf("1.3")) {
654: Message.debug("post 1.3 ivy file: using "
655: + PatternMatcher.EXACT + " as default matcher");
656: defaultMatcher = ivy.getMatcher(PatternMatcher.EXACT);
657: } else {
658: Message.debug("pre 1.3 ivy file: using "
659: + PatternMatcher.EXACT_OR_REGEXP
660: + " as default matcher");
661: defaultMatcher = ivy
662: .getMatcher(PatternMatcher.EXACT_OR_REGEXP);
663: }
664:
665: for (int i = 0; i < attributes.getLength(); i++) {
666: if (attributes.getQName(i).startsWith("xmlns:")) {
667: md.addExtraAttributeNamespace(attributes
668: .getQName(i).substring("xmlns:".length()),
669: attributes.getValue(i));
670: }
671: }
672: }
673:
674: private void addDependencyArtifacts(String tag,
675: Attributes attributes) throws MalformedURLException {
676: state = DEP_ARTIFACT;
677: parseRule(tag, attributes);
678: }
679:
680: private void addIncludeRule(String tag, Attributes attributes)
681: throws MalformedURLException {
682: state = ARTIFACT_INCLUDE;
683: parseRule(tag, attributes);
684: }
685:
686: private void addExcludeRule(String tag, Attributes attributes)
687: throws MalformedURLException {
688: state = ARTIFACT_EXCLUDE;
689: parseRule(tag, attributes);
690: }
691:
692: private void parseRule(String tag, Attributes attributes)
693: throws MalformedURLException {
694: String name = ivy.substitute(attributes.getValue("name"));
695: if (name == null) {
696: name = ivy.substitute(attributes.getValue("artifact"));
697: if (name == null) {
698: name = "artifact".equals(tag) ? dd
699: .getDependencyId().getName()
700: : PatternMatcher.ANY_EXPRESSION;
701: }
702: }
703: String type = ivy.substitute(attributes.getValue("type"));
704: if (type == null) {
705: type = "artifact".equals(tag) ? "jar"
706: : PatternMatcher.ANY_EXPRESSION;
707: }
708: String ext = ivy.substitute(attributes.getValue("ext"));
709: ext = ext != null ? ext : type;
710: if (state == DEP_ARTIFACT) {
711: String url = ivy.substitute(attributes.getValue("url"));
712: Map extraAtt = ExtendableItemHelper.getExtraAttributes(
713: attributes, new String[] { "name", "type",
714: "ext", "url", "conf" });
715: confAware = new DefaultDependencyArtifactDescriptor(
716: name, type, ext, url == null ? null : new URL(
717: url), extraAtt);
718: } else if (state == ARTIFACT_INCLUDE) {
719: PatternMatcher matcher = getPatternMatcher(attributes
720: .getValue("matcher"));
721: String org = ivy.substitute(attributes.getValue("org"));
722: org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
723: String module = ivy.substitute(attributes
724: .getValue("module"));
725: module = module == null ? PatternMatcher.ANY_EXPRESSION
726: : module;
727: ArtifactId aid = new ArtifactId(new ModuleId(org,
728: module), name, type, ext);
729: Map extraAtt = ExtendableItemHelper.getExtraAttributes(
730: attributes, new String[] { "org", "module",
731: "name", "type", "ext", "matcher",
732: "conf" });
733: confAware = new DefaultIncludeRule(aid, matcher,
734: extraAtt);
735: } else { // _state == ARTIFACT_EXCLUDE || EXCLUDE
736: PatternMatcher matcher = getPatternMatcher(attributes
737: .getValue("matcher"));
738: String org = ivy.substitute(attributes.getValue("org"));
739: org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
740: String module = ivy.substitute(attributes
741: .getValue("module"));
742: module = module == null ? PatternMatcher.ANY_EXPRESSION
743: : module;
744: ArtifactId aid = new ArtifactId(new ModuleId(org,
745: module), name, type, ext);
746: Map extraAtt = ExtendableItemHelper.getExtraAttributes(
747: attributes, new String[] { "org", "module",
748: "name", "type", "ext", "matcher",
749: "conf" });
750: confAware = new DefaultExcludeRule(aid, matcher,
751: extraAtt);
752: }
753: String confs = ivy.substitute(attributes.getValue("conf"));
754: // only add confs if they are specified. if they aren't, endElement will handle this
755: // only if there are no conf defined in sub elements
756: if (confs != null && confs.length() > 0) {
757: String[] conf;
758: if ("*".equals(confs)) {
759: conf = md.getConfigurationsNames();
760: } else {
761: conf = confs.split(",");
762: }
763: for (int i = 0; i < conf.length; i++) {
764: addConfiguration(conf[i].trim());
765: }
766: }
767: }
768:
769: private void addConfiguration(String c) {
770: confAware.addConfiguration(c);
771: if (state == EXCLUDE) {
772: // we are adding a configuration to a module wide exclude rule
773: // we have nothing special to do here, the rule has already been added to the module
774: // descriptor
775: } else {
776: // we are currently adding a configuration to either an include, exclude or artifact
777: // element
778: // of a dependency. This means that we have to add this element to the corresponding
779: // conf
780: // of the current dependency descriptor
781: if (confAware instanceof DependencyArtifactDescriptor) {
782: dd.addDependencyArtifact(c,
783: (DependencyArtifactDescriptor) confAware);
784: } else if (confAware instanceof IncludeRule) {
785: dd.addIncludeRule(c, (IncludeRule) confAware);
786: } else if (confAware instanceof ExcludeRule) {
787: dd.addExcludeRule(c, (ExcludeRule) confAware);
788: }
789: }
790: }
791:
792: private PatternMatcher getPatternMatcher(String m) {
793: String matcherName = ivy.substitute(m);
794: PatternMatcher matcher = matcherName == null ? defaultMatcher
795: : ivy.getMatcher(matcherName);
796: if (matcher == null) {
797: throw new IllegalArgumentException("unknown matcher "
798: + matcherName);
799: }
800: return matcher;
801: }
802:
803: public void characters(char[] ch, int start, int length)
804: throws SAXException {
805: if (buffer != null) {
806: buffer.append(ch, start, length);
807: }
808: }
809:
810: public void endElement(String uri, String localName,
811: String qName) throws SAXException {
812: if (state == PUB && "artifact".equals(qName)
813: && artifact.getConfigurations().length == 0) {
814: String[] confs = md.getConfigurationsNames();
815: for (int i = 0; i < confs.length; i++) {
816: artifact.addConfiguration(confs[i]);
817: md.addArtifact(confs[i], artifact);
818: }
819: } else if ("configurations".equals(qName)) {
820: checkConfigurations();
821: } else if ((state == DEP_ARTIFACT && "artifact"
822: .equals(qName))
823: || (state == ARTIFACT_INCLUDE && "include"
824: .equals(qName))
825: || (state == ARTIFACT_EXCLUDE && "exclude"
826: .equals(qName))) {
827: state = DEP;
828: if (confAware.getConfigurations().length == 0) {
829: String[] confs = md.getConfigurationsNames();
830: for (int i = 0; i < confs.length; i++) {
831: addConfiguration(confs[i]);
832: }
833: }
834: confAware = null;
835: } else if (state == EXCLUDE) {
836: if (confAware.getConfigurations().length == 0) {
837: String[] confs = md.getConfigurationsNames();
838: for (int i = 0; i < confs.length; i++) {
839: addConfiguration(confs[i]);
840: }
841: }
842: confAware = null;
843: state = DEPS;
844: } else if ("dependency".equals(qName)) {
845: if (dd.getModuleConfigurations().length == 0) {
846: parseDepsConfs(getDefaultConf(), dd);
847: }
848: state = DEPS;
849: } else if ("dependencies".equals(qName)) {
850: state = NONE;
851: } else if (state == INFO && "info".equals(qName)) {
852: state = NONE;
853: } else if (state == INFO && "description".equals(qName)) {
854: state = INFO;
855: } else if (state == INFO) {
856: md.addExtraInfo(qName, buffer == null ? "" : buffer
857: .toString());
858: buffer = null;
859: }
860: }
861:
862: private void checkConfigurations() {
863: if (md.getConfigurations().length == 0) {
864: md.addConfiguration(new Configuration("default"));
865: }
866: }
867:
868: private void replaceConfigurationWildcards() {
869: Configuration[] configs = md.getConfigurations();
870: for (int i = 0; i < configs.length; i++) {
871: configs[i].replaceWildcards(md);
872: }
873: }
874:
875: }
876:
877: public String toString() {
878: return "ivy parser";
879: }
880: }
|