/*
 *  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.module.descriptor;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.module.id.ModuleRules;
import org.apache.ivy.core.module.status.StatusManager;
import org.apache.ivy.plugins.conflict.ConflictManager;
import org.apache.ivy.plugins.matcher.MapMatcher;
import org.apache.ivy.plugins.matcher.MatcherHelper;
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.namespace.NamespaceTransformer;
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.version.VersionMatcher;
import org.apache.ivy.util.Message;

/**
 *
 */
public class DefaultModuleDescriptor implements ModuleDescriptor {

    public static DefaultModuleDescriptor newDefaultInstance(ModuleRevisionId mrid) {
        return newDefaultInstance(mrid, null);
    }

    public static DefaultModuleDescriptor newCallerInstance(ModuleRevisionId mrid, String[] confs,
            boolean transitive, boolean changing) {
        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(
                ModuleRevisionId.newInstance(mrid.getOrganisation(), mrid.getName() + "-caller",
                    "working"), "integration", null, true);
        for (int i = 0; i < confs.length; i++) {
            moduleDescriptor.addConfiguration(new Configuration(confs[i]));
        }
        moduleDescriptor.setLastModified(System.currentTimeMillis());
        DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(moduleDescriptor, mrid,
                true, changing, transitive);
        for (int j = 0; j < confs.length; j++) {
            dd.addDependencyConfiguration(confs[j], confs[j]);
        }
        moduleDescriptor.addDependency(dd);

        return moduleDescriptor;
    }

    public static DefaultModuleDescriptor newCallerInstance(ModuleRevisionId[] mrid,
            boolean transitive, boolean changing) {
        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(
                ModuleRevisionId.newInstance("caller", "all-caller", "working"), "integration",
                null, true);
        moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
        moduleDescriptor.setLastModified(System.currentTimeMillis());
        for (int i = 0; i < mrid.length; i++) {
            DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(moduleDescriptor,
                    mrid[i], true, changing, transitive);
            dd.addDependencyConfiguration(DEFAULT_CONFIGURATION, "*");
            moduleDescriptor.addDependency(dd);
        }

        return moduleDescriptor;
    }

    public static DefaultModuleDescriptor newDefaultInstance(ModuleRevisionId mrid,
            DependencyArtifactDescriptor[] artifacts) {
        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(mrid, "release",
                null, true);
        moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
        if (artifacts != null && artifacts.length > 0) {
            for (int i = 0; i < artifacts.length; i++) {
                moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION,
                    new MDArtifact(moduleDescriptor, artifacts[i].getName(),
                            artifacts[i].getType(), artifacts[i].getExt(), artifacts[i].getUrl(),
                            artifacts[i].getExtraAttributes()));
            }
        } else {
            moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION, new MDArtifact(moduleDescriptor,
                    mrid.getName(), "jar", "jar"));
        }
        moduleDescriptor.setLastModified(System.currentTimeMillis());
        return moduleDescriptor;
    }

    public static DefaultModuleDescriptor newBasicInstance(ModuleRevisionId mrid,
            Date publicationDate) {
        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(mrid, "release",
                publicationDate, false);
        moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
        moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION,
            new MDArtifact(moduleDescriptor, mrid.getName(), "jar", "jar"));
        return moduleDescriptor;
    }

    /**
     * Transforms the given module descriptor of the given namespace and return a new module
     * descriptor in the system namespace. <i>Note that dependency exclude rules are not converted
     * in system namespace, because they aren't transformable (the name space hasn't the ability to
     * convert regular expressions)</i>
     * 
     * @param md
     * @param ns
     * @return
     */
    public static ModuleDescriptor transformInstance(ModuleDescriptor md, Namespace ns) {
        NamespaceTransformer t = ns.getToSystemTransformer();
        if (t.isIdentity()) {
            return md;
        }
        DefaultModuleDescriptor nmd = new DefaultModuleDescriptor(md.getParser(), md.getResource());
        nmd.revId = t.transform(md.getModuleRevisionId());
        nmd.resolvedRevId = t.transform(md.getResolvedModuleRevisionId());
        nmd.status = md.getStatus();
        nmd.publicationDate = md.getPublicationDate();
        nmd.resolvedPublicationDate = md.getResolvedPublicationDate();

        ExtendsDescriptor[] ed = md.getInheritedDescriptors();
        for (int i = 0; i < ed.length; ++i) {
            ModuleRevisionId mrid = t.transform(ed[i].getParentRevisionId());
            ModuleRevisionId resolvedMrid = t.transform(ed[i].getResolvedParentRevisionId());
            nmd.inheritedDescriptors.add(new DefaultExtendsDescriptor(mrid, resolvedMrid, ed[i]
                    .getLocation(), ed[i].getExtendsTypes()));
        }

        DependencyDescriptor[] dd = md.getDependencies();
        for (int i = 0; i < dd.length; i++) {
            nmd.dependencies.add(NameSpaceHelper.toSystem(dd[i], ns));
        }
        Configuration[] confs = md.getConfigurations();
        for (int i = 0; i < confs.length; i++) {
            nmd.configurations.put(confs[i].getName(), confs[i]);
            Artifact[] arts = md.getArtifacts(confs[i].getName());
            for (int j = 0; j < arts.length; j++) {
                nmd.addArtifact(confs[i].getName(), NameSpaceHelper.transform(arts[j], t));
            }
        }
        nmd.setDefault(md.isDefault());
        if (md instanceof DefaultModuleDescriptor) {
            DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
            nmd.conflictManagers = (ModuleRules) dmd.conflictManagers.clone();
            nmd.dependencyDescriptorMediators = (ModuleRules) dmd.dependencyDescriptorMediators
                    .clone();
        } else {
            Message.warn("transformed module descriptor is not a default module descriptor: "
                    + "impossible to copy conflict manager and version mediation configuration: "
                    + md);
        }
        nmd.licenses.addAll(Arrays.asList(md.getLicenses()));
        nmd.homePage = md.getHomePage();
        nmd.description = md.getDescription();
        nmd.lastModified = md.getLastModified();
        nmd.extraAttributesNamespaces = md.getExtraAttributesNamespaces();
        nmd.extraInfo = md.getExtraInfo();
        nmd.namespace = ns;

        return nmd;
    }

    private ModuleRevisionId revId;

    private ModuleRevisionId resolvedRevId;

    private String status = StatusManager.getCurrent().getDefaultStatus();

    private Date publicationDate;

    private Date resolvedPublicationDate;

    private List dependencies = new ArrayList(); // List (DependencyDescriptor)

    private Map configurations = new LinkedHashMap(); // Map(String conf -> Configuration)

    private Map artifactsByConf = new HashMap(); // Map (String conf -> Collection(Artifact))

    private Collection artifacts = new LinkedHashSet(); // Collection(Artifact)

    // all artifacts could also be found in the artifactsByConf map, but here we can
    // preserve the order

    private boolean isDefault = false;

    private ModuleRules conflictManagers = new ModuleRules();

    private ModuleRules dependencyDescriptorMediators = new ModuleRules();

    private List licenses = new ArrayList(); // List(License)

    private String homePage;

    private String description = "";

    private long lastModified = 0;

    private Namespace namespace;

    private String defaultConf;

    private String defaultConfMapping;

    private boolean mappingOverride;

    private ModuleDescriptorParser parser;

    private Resource resource;

    private List excludeRules = new ArrayList(); // List(ExcludeRule)

    private Artifact metadataArtifact;

    private List inheritedDescriptors = new ArrayList(); // List(ExtendsDescriptor)

    private Map/* <String,String> */extraAttributesNamespaces = new LinkedHashMap();

    private Map/* <String,String> */extraInfo = new HashMap();

    public DefaultModuleDescriptor(ModuleRevisionId id, String status, Date pubDate) {
        this(id, status, pubDate, false);
    }

    public DefaultModuleDescriptor(ModuleRevisionId id, String status, Date pubDate,
            boolean isDefault) {
        if (id == null) {
            throw new NullPointerException("null module revision id not allowed");
        }
        if (status == null) {
            throw new NullPointerException("null status not allowed");
        }
        this.revId = id;
        this.resolvedRevId = id;
        this.status = status;
        this.publicationDate = pubDate;
        this.resolvedPublicationDate = publicationDate == null ? new Date() : publicationDate;
        this.isDefault = isDefault;
        this.parser = XmlModuleDescriptorParser.getInstance();
    }

    /**
     * IMPORTANT : at least call setModuleRevisionId and setResolvedPublicationDate with instances
     * created by this constructor !
     */
    public DefaultModuleDescriptor(ModuleDescriptorParser parser, Resource res) {
        this.parser = parser;
        resource = res;
    }

    public Artifact getMetadataArtifact() {
        if (metadataArtifact == null) {
            metadataArtifact = DefaultArtifact.newIvyArtifact(resolvedRevId,
                resolvedPublicationDate);
        }
        return metadataArtifact;
    }

    public void setModuleArtifact(Artifact moduleArtifact) {
        this.metadataArtifact = moduleArtifact;
    }

    public boolean isDefault() {
        return isDefault;
    }

    public void setPublicationDate(Date publicationDate) {
        this.publicationDate = publicationDate;
        if (resolvedPublicationDate == null) {
            resolvedPublicationDate = publicationDate == null ? new Date() : publicationDate;
        }
    }

    public Date getPublicationDate() {
        return publicationDate;
    }

    public void setResolvedPublicationDate(Date publicationDate) {
        if (publicationDate == null) {
            throw new NullPointerException("null publication date not allowed");
        }
        resolvedPublicationDate = publicationDate;
    }

    public Date getResolvedPublicationDate() {
        return resolvedPublicationDate;
    }

    public String getRevision() {
        return getResolvedModuleRevisionId().getRevision();
    }

    public void setModuleRevisionId(ModuleRevisionId revId) {
        if (revId == null) {
            throw new NullPointerException("null module revision id not allowed");
        }
        this.revId = revId;
        if (resolvedRevId == null) {
            resolvedRevId = revId;
        }
    }

    public void setResolvedModuleRevisionId(ModuleRevisionId revId) {
        resolvedRevId = revId;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void addInheritedDescriptor(ExtendsDescriptor descriptor) {
        inheritedDescriptors.add(descriptor);
    }

    public void addDependency(DependencyDescriptor dependency) {
        dependencies.add(dependency);
    }

    public void addConfiguration(Configuration conf) {
        configurations.put(conf.getName(), conf);
    }

    /**
     * Artifact configurations are not used since added artifact may not be entirely completed, so
     * its configurations data may not be accurate
     * 
     * @param conf
     * @param artifact
     */
    public void addArtifact(String conf, Artifact artifact) {
        Configuration c = getConfiguration(conf);
        if (c == null) {
            throw new IllegalArgumentException("Cannot add artifact '"
                    + artifact.getId().getArtifactId().getShortDescription()
                    + "' to configuration '" + conf + "' of module " + revId
                    + " because this configuration doesn't exist!");
        }
        if (c instanceof ConfigurationGroup) {
            ConfigurationGroup group = (ConfigurationGroup) c;
            String[] members = group.getMembersConfigurationNames();
            for (int i = 0; i < members.length; i++) {
                addArtifact(members[i], artifact);
            }
        } else {
            Collection artifacts = (Collection) artifactsByConf.get(conf);
            if (artifacts == null) {
                artifacts = new ArrayList();
                artifactsByConf.put(conf, artifacts);
            }
            artifacts.add(artifact);
            this.artifacts.add(artifact);
        }
    }

    public ModuleRevisionId getModuleRevisionId() {
        return revId;
    }

    public ModuleRevisionId getResolvedModuleRevisionId() {
        return resolvedRevId;
    }

    public String getStatus() {
        return status;
    }

    public ExtendsDescriptor[] getInheritedDescriptors() {
        return (ExtendsDescriptor[]) inheritedDescriptors
                .toArray(new ExtendsDescriptor[inheritedDescriptors.size()]);
    }

    public Configuration[] getConfigurations() {
        return (Configuration[]) configurations.values().toArray(
            new Configuration[configurations.size()]);
    }

    public String[] getConfigurationsNames() {
        return (String[]) configurations.keySet().toArray(new String[configurations.size()]);
    }

    public String[] getPublicConfigurationsNames() {
        List ret = new ArrayList();
        for (Iterator iter = configurations.values().iterator(); iter.hasNext();) {
            Configuration conf = (Configuration) iter.next();
            if (conf.getVisibility() == Configuration.Visibility.PUBLIC) {
                ret.add(conf.getName());
            }
        }
        return (String[]) ret.toArray(new String[ret.size()]);
    }

    /**
     * Returns the configuration object with the given name in the current module descriptor, null
     * if not found.
     */
    public Configuration getConfiguration(String confName) {
        Configuration configuration = (Configuration) configurations.get(confName);
        if (configuration == null && confName != null) {
            // let's first check if the configuration is a conf group
            Matcher m = Pattern.compile("\\*\\[([^=]+)\\=([^\\]]+)\\]").matcher(confName);
            if (m.matches()) {
                String attName = m.group(1);
                String attValue = m.group(2);

                // this is a conf group, let's search for its members
                Map /* <String,Configuration> */members = new LinkedHashMap();
                for (Iterator it = configurations.values().iterator(); it.hasNext();) {
                    Configuration conf = (Configuration) it.next();
                    if (attValue.equals(conf.getAttribute(attName))) {
                        members.put(conf.getName(), conf);
                    }
                }
                return new ConfigurationGroup(confName, members);
            }

            // let's see if a configuration intersection is requested
            String[] confs = confName.split("\\+");
            if (confs.length <= 1) {
                return null;
            }
            Map /* <String,Configuration> */intersectedConfs = new LinkedHashMap();
            for (int i = 0; i < confs.length; i++) {
                Configuration c = (Configuration) configurations.get(confs[i]);
                if (c == null) {
                    Message.verbose("missing configuration '" + confs[i] + "' from intersection "
                            + confName + " in " + this);
                    return null;
                }
                intersectedConfs.put(confs[i], c);
            }
            return new ConfigurationIntersection(confName, intersectedConfs);
        }
        return configuration;
    }

    public Artifact[] getArtifacts(String conf) {
        Configuration c = getConfiguration(conf);
        if (c == null) {
            return new Artifact[0];
        }
        Collection artifacts = (Collection) artifactsByConf.get(conf);
        if (c instanceof ConfigurationIntersection) {
            ConfigurationIntersection intersection = (ConfigurationIntersection) c;
            String[] intersected = intersection.getIntersectedConfigurationNames();
            Set/* <Artifact> */intersectedArtifacts = new LinkedHashSet();
            for (int j = 0; j < intersected.length; j++) {
                Collection arts = getArtifactsIncludingExtending(intersected[j]);
                if (intersectedArtifacts.isEmpty()) {
                    intersectedArtifacts.addAll(arts);
                } else {
                    intersectedArtifacts.retainAll(arts);
                }
            }
            if (artifacts != null) {
                intersectedArtifacts.addAll(artifacts);
            }
            return (Artifact[]) intersectedArtifacts.toArray(new Artifact[intersectedArtifacts
                    .size()]);
        } else if (c instanceof ConfigurationGroup) {
            ConfigurationGroup group = (ConfigurationGroup) c;
            String[] members = group.getMembersConfigurationNames();
            Set/* <Artifact> */groupArtifacts = new LinkedHashSet();
            for (int i = 0; i < members.length; i++) {
                groupArtifacts.addAll(getArtifactsIncludingExtending(members[i]));
            }
            if (artifacts != null) {
                groupArtifacts.addAll(artifacts);
            }
            return (Artifact[]) groupArtifacts.toArray(new Artifact[groupArtifacts.size()]);
        } else {
            if (artifacts == null) {
                return new Artifact[0];
            } else {
                return (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]);
            }
        }
    }

    private Collection/* <Artifact> */getArtifactsIncludingExtending(String conf) {
        Collection extendingConfs = Configuration.findConfigurationExtending(conf,
            getConfigurations());
        Set/* <Artifact> */artifacts = new LinkedHashSet();
        Collection arts = (Collection) artifactsByConf.get(conf);
        if (arts != null) {
            artifacts.addAll(arts);
        }
        for (Iterator it = extendingConfs.iterator(); it.hasNext();) {
            Configuration extendingConf = (Configuration) it.next();
            arts = (Collection) artifactsByConf.get(extendingConf.getName());
            if (arts != null) {
                artifacts.addAll(arts);
            }
        }
        return artifacts;
    }

    public Artifact[] getAllArtifacts() {
        return (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]);
    }

    public DependencyDescriptor[] getDependencies() {
        return (DependencyDescriptor[]) dependencies.toArray(new DependencyDescriptor[dependencies
                .size()]);
    }

    public boolean dependsOn(VersionMatcher matcher, ModuleDescriptor md) {
        for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
            DependencyDescriptor dd = (DependencyDescriptor) iter.next();
            if (dd.getDependencyId().equals(md.getModuleRevisionId().getModuleId())) {
                if (md.getResolvedModuleRevisionId().getRevision() == null) {
                    return true;
                } else if (matcher.accept(dd.getDependencyRevisionId(), md)) {
                    return true;
                }
            }
        }
        return false;
    }

    public void toIvyFile(File destFile) throws ParseException, IOException {
        if (parser != null && resource != null) {
            parser.toIvyFile(resource.openStream(), resource, destFile, this);
        } else {
            XmlModuleDescriptorWriter.write(this, destFile);
        }
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((revId == null) ? 0 : revId.hashCode());
        return result;
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        DefaultModuleDescriptor other = (DefaultModuleDescriptor) obj;
        if (revId == null) {
            if (other.revId != null) {
                return false;
            }
        } else if (!revId.equals(other.revId)) {
            return false;
        }
        return true;
    }

    public String toString() {
        return "module: " + revId + " status=" + status + " publication=" + publicationDate
                + " configurations=" + configurations + " artifacts=" + artifactsByConf
                + " dependencies=" + dependencies;
    }

    public void setDefault(boolean b) {
        isDefault = b;
    }

    /**
     * regular expressions as explained in Pattern class may be used in ModuleId organisation and
     * name
     * 
     * @param moduleId
     * @param matcher
     * @param resolverName
     */
    public void addConflictManager(ModuleId moduleId, PatternMatcher matcher,
            ConflictManager manager) {
        conflictManagers.defineRule(new MapMatcher(moduleId.getAttributes(), matcher), manager);
    }

    public ConflictManager getConflictManager(ModuleId moduleId) {
        return (ConflictManager) conflictManagers.getRule(moduleId);
    }

    public void addDependencyDescriptorMediator(ModuleId moduleId, PatternMatcher matcher,
            DependencyDescriptorMediator ddm) {
        dependencyDescriptorMediators.defineRule(new MapMatcher(moduleId.getAttributes(), matcher),
            ddm);
    }

    public DependencyDescriptor mediate(DependencyDescriptor dd) {
        Object[] mediators = dependencyDescriptorMediators.getRules(dd.getDependencyId());
        for (int i = 0; i < mediators.length; i++) {
            dd = ((DependencyDescriptorMediator) mediators[i]).mediate(dd);
        }
        return dd;
    }

    public ModuleRules/* <DependencyDescriptorMediator> */getAllDependencyDescriptorMediators() {
        return (ModuleRules) dependencyDescriptorMediators.clone();
    }

    public void addLicense(License license) {
        licenses.add(license);
    }

    public License[] getLicenses() {
        return (License[]) licenses.toArray(new License[licenses.size()]);
    }

    public String getHomePage() {
        return homePage;
    }

    public void setHomePage(String homePage) {
        this.homePage = homePage;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public long getLastModified() {
        return lastModified;
    }

    public void setLastModified(long lastModified) {
        this.lastModified = lastModified;
    }

    public Namespace getNamespace() {
        return namespace;
    }

    public boolean isNamespaceUseful() {
        for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
            DependencyDescriptor dd = (DependencyDescriptor) iter.next();
            if (dd.getAllExcludeRules().length > 0) {
                return true;
            }
        }
        return false;
    }

    public void setNamespace(Namespace ns) {
        namespace = ns;
    }

    /**
     * Throws an exception if the module descriptor is inconsistent For the moment, only extended
     * configurations existence and cycles are checked
     */
    public void check() {
        Stack confs = new Stack();
        for (Iterator iter = configurations.values().iterator(); iter.hasNext();) {
            Configuration conf = (Configuration) iter.next();
            String[] ext = conf.getExtends();
            for (int i = 0; i < ext.length; i++) {
                confs.push(conf.getName());
                checkConf(confs, ext[i].trim());
                confs.pop();
            }
        }
    }

    private void checkConf(Stack confs, String confName) {
        int index = confs.indexOf(confName);
        if (index != -1) {
            StringBuffer cycle = new StringBuffer();
            for (; index < confs.size(); index++) {
                cycle.append(confs.get(index)).append(" => ");
            }
            cycle.append(confName);
            throw new IllegalStateException("illegal cycle detected in configuration extension: "
                    + cycle);
        }
        Configuration conf = getConfiguration(confName);
        if (conf == null) {
            throw new IllegalStateException("unknown configuration '" + confName
                    + "'. It is extended by " + confs.get(confs.size() - 1));
        }
        String[] ext = conf.getExtends();
        for (int i = 0; i < ext.length; i++) {
            confs.push(conf.getName());
            checkConf(confs, ext[i].trim());
            confs.pop();
        }
    }

    public String getDefaultConf() {
        return defaultConf;
    }

    public void setDefaultConf(String defaultConf) {
        this.defaultConf = defaultConf;
    }

    public String getDefaultConfMapping() {
        return defaultConfMapping;
    }

    public void setDefaultConfMapping(String defaultConfMapping) {
        this.defaultConfMapping = defaultConfMapping;
    }

    public void setMappingOverride(boolean override) {
        mappingOverride = override;
    }

    public boolean isMappingOverride() {
        return mappingOverride;
    }

    public String getAttribute(String attName) {
        return resolvedRevId.getAttribute(attName);
    }

    public Map getAttributes() {
        return resolvedRevId.getAttributes();
    }

    public String getExtraAttribute(String attName) {
        return resolvedRevId.getExtraAttribute(attName);
    }

    public Map getExtraAttributes() {
        return resolvedRevId.getExtraAttributes();
    }

    public Map getQualifiedExtraAttributes() {
        return resolvedRevId.getQualifiedExtraAttributes();
    }

    public ModuleDescriptorParser getParser() {
        return parser;
    }

    public Resource getResource() {
        return resource;
    }

    public void addExcludeRule(ExcludeRule rule) {
        excludeRules.add(rule);
    }

    public boolean canExclude() {
        return !excludeRules.isEmpty();
    }

    /**
     * only works when namespace is properly set. The behaviour is not specified if namespace is not
     * set
     */
    public boolean doesExclude(String[] moduleConfigurations, ArtifactId artifactId) {
        if (namespace != null) {
            artifactId = NameSpaceHelper
                    .transform(artifactId, namespace.getFromSystemTransformer());
        }
        ExcludeRule[] rules = getExcludeRules(moduleConfigurations);
        for (int i = 0; i < rules.length; i++) {
            if (MatcherHelper.matches(rules[i].getMatcher(), rules[i].getId(), artifactId)) {
                return true;
            }
        }
        return false;
    }

    public ExcludeRule[] getAllExcludeRules() {
        return (ExcludeRule[]) excludeRules.toArray(new ExcludeRule[excludeRules.size()]);
    }

    public ExcludeRule[] getExcludeRules(String[] moduleConfigurations) {
        Set rules = new LinkedHashSet();
        for (Iterator iter = excludeRules.iterator(); iter.hasNext();) {
            ExcludeRule rule = (ExcludeRule) iter.next();
            String[] ruleConfs = rule.getConfigurations();
            if (containsAny(ruleConfs, moduleConfigurations)) {
                rules.add(rule);
            }
        }
        return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
    }

    private boolean containsAny(String[] arr1, String[] arr2) {
        return new ArrayList(Arrays.asList(arr1)).removeAll(Arrays.asList(arr2));
    }

    public Map getExtraAttributesNamespaces() {
        return extraAttributesNamespaces;
    }

    public void addExtraAttributeNamespace(String prefix, String namespace) {
        extraAttributesNamespaces.put(prefix, namespace);
    }

    public void addExtraInfo(String infoKey, String value) {
        extraInfo.put(infoKey, value);
    }

    public Map getExtraInfo() {
        return extraInfo;
    }
}
