/*
 * 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.maven.archetype.old.descriptor;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;

import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

/**
 * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
 * @version $Id$
 */
public class ArchetypeDescriptorBuilder {
    public ArchetypeDescriptor build(Reader reader) throws IOException, XmlPullParserException {
        ArchetypeDescriptor descriptor = new ArchetypeDescriptor();

        Xpp3Dom dom = Xpp3DomBuilder.build(reader);

        descriptor.setId(dom.getChild("id").getValue());

        Xpp3Dom allowPartialDom = dom.getChild("allowPartial");

        if (allowPartialDom != null) {
            String allowPartial = allowPartialDom.getValue();

            if ("true".equals(allowPartial) || "1".equals(allowPartial) || "on".equals(allowPartial)) {
                descriptor.setAllowPartial(true);
            }
        }

        // ----------------------------------------------------------------------
        // Main
        // ----------------------------------------------------------------------

        Xpp3Dom sources = dom.getChild("sources");

        if (sources != null) {
            Xpp3Dom[] sourceList = sources.getChildren("source");

            for (int i = 0; i < sourceList.length; i++) {
                addSourceToDescriptor(sourceList[i], descriptor);
            }
        }

        Xpp3Dom resources = dom.getChild("resources");

        if (resources != null) {
            Xpp3Dom[] resourceList = resources.getChildren("resource");

            for (int i = 0; i < resourceList.length; i++) {
                addResourceToDescriptor(resourceList[i], descriptor);
            }
        }

        // ----------------------------------------------------------------------
        // Test
        // ----------------------------------------------------------------------

        Xpp3Dom testSources = dom.getChild("testSources");

        if (testSources != null) {
            Xpp3Dom[] testSourceList = testSources.getChildren("source");

            for (int i = 0; i < testSourceList.length; i++) {
                addTestSourceToDescriptor(testSourceList[i], descriptor);
            }
        }

        Xpp3Dom testResources = dom.getChild("testResources");

        if (testResources != null) {
            Xpp3Dom[] testResourceList = testResources.getChildren("resource");

            for (int i = 0; i < testResourceList.length; i++) {
                addTestResourceToDescriptor(testResourceList[i], descriptor);
            }
        }

        // ----------------------------------------------------------------------
        // Site
        // ----------------------------------------------------------------------

        Xpp3Dom siteResources = dom.getChild("siteResources");

        if (siteResources != null) {
            Xpp3Dom[] siteResourceList = siteResources.getChildren("resource");

            for (int i = 0; i < siteResourceList.length; i++) {
                addSiteResourceToDescriptor(siteResourceList[i], descriptor);
            }
        }

        return descriptor;
    }

    /**
     * Adds the source element <code>source</code> to the list of sources in the
     * <code>descriptor</code> and sets its <code>TemplateDescriptor</code> to
     * <i>filtered</i> and with the encoding specified in the <code>encoding</code>
     * attribute or the Java virtual machine's default if it is not defined.
     *
     * @param source     a <code>&lt;source&gt;</code> element from the <code>&lt;sources&gt;</code>
     * @param descriptor the <code>ArchetypeDescriptor</code> to add the source template to
     * @throws XmlPullParserException if the encoding specified is not valid or supported
     */
    private static void addSourceToDescriptor(Xpp3Dom source, ArchetypeDescriptor descriptor)
            throws XmlPullParserException {
        descriptor.addSource(source.getValue());

        TemplateDescriptor sourceDesc = descriptor.getSourceDescriptor(source.getValue());

        sourceDesc.setFiltered(true);

        if (source.getAttribute("encoding") != null) {
            try {
                sourceDesc.setEncoding(source.getAttribute("encoding"));
            } catch (IllegalCharsetNameException icne) {
                throw new XmlPullParserException(source.getAttribute("encoding") + " is not a valid encoding.");
            } catch (UnsupportedCharsetException uce) {
                throw new XmlPullParserException(source.getAttribute("encoding") + " is not a supported encoding.");
            }
        }
    }

    /**
     * Adds the resource element <code>resource</code> to the list of resources in the
     * <code>descriptor</code> and sets its <code>TemplateDescriptor</code> to
     * <i>filtered</i> if the attribute <code>filtered</code> was not
     * specified or its value is <code>&quot;true&quot;</code>, or <code>false</code>
     * if its value is <code>&quot;false&quot;</code>, and the encoding specified
     * in the <code>encoding</code> attribute or the Java virtual machine's default if
     * it is not defined. If the <code>resource</code> is a property file (ends in
     * <code>.properties</code>) its encoding will be set to <code>iso-8859-1</code>
     * even if some other encoding is specified in the attribute.
     *
     * @param resource   a <code>&lt;resource&gt;</code> element from the <code>&lt;resources&gt;</code>
     * @param descriptor the <code>ArchetypeDescriptor</code> to add the resource template to
     * @throws XmlPullParserException if the encoding specified is not valid or supported or if the
     *                                value of the attribute <code>filtered</code> is no valid
     */
    private static void addResourceToDescriptor(Xpp3Dom resource, ArchetypeDescriptor descriptor)
            throws XmlPullParserException {
        descriptor.addResource(resource.getValue());

        if (resource.getAttribute("filtered") != null) {
            TemplateDescriptor resourceDesc = descriptor.getResourceDescriptor(resource.getValue());

            try {
                resourceDesc.setFiltered(getValueFilteredAttribute(resource.getAttribute("filtered")));
            } catch (IllegalArgumentException iae) {
                throw new XmlPullParserException(iae.getMessage());
            }
        }

        if (resource.getAttribute("encoding") != null) {
            TemplateDescriptor resourceDesc = descriptor.getResourceDescriptor(resource.getValue());

            try {
                resourceDesc.setEncoding(resource.getAttribute("encoding"));
            } catch (IllegalCharsetNameException icne) {
                throw new XmlPullParserException(resource.getAttribute("encoding") + " is not a valid encoding.");
            } catch (UnsupportedCharsetException uce) {
                throw new XmlPullParserException(resource.getAttribute("encoding") + " is not a supported encoding.");
            }
        }

        if (resource.getValue().endsWith(".properties")) {
            TemplateDescriptor resourceDesc = descriptor.getResourceDescriptor(resource.getValue());

            resourceDesc.setEncoding("iso-8859-1");
        }
    }

    /**
     * Adds the test-source element <code>source</code> to the list of sources in the
     * <code>descriptor</code> and sets its <code>TemplateDescriptor</code> to
     * <i>filtered</i> and with the encoding specified in the <code>encoding</code>
     * attribute or the Java virtual machine's default if it is not defined.
     *
     * @param testSource a <code>&lt;source&gt;</code> element from the <code>&lt;testSources&gt;</code>
     * @param descriptor the <code>ArchetypeDescriptor</code> to add the test-source template to
     * @throws XmlPullParserException if the encoding specified is not valid or supported
     */
    private static void addTestSourceToDescriptor(Xpp3Dom testSource, ArchetypeDescriptor descriptor)
            throws XmlPullParserException {
        descriptor.addTestSource(testSource.getValue());
        TemplateDescriptor testSourceDesc = descriptor.getTestSourceDescriptor(testSource.getValue());
        testSourceDesc.setFiltered(true);
        if (testSource.getAttribute("encoding") != null) {
            try {
                testSourceDesc.setEncoding(testSource.getAttribute("encoding"));
            } catch (IllegalCharsetNameException icne) {
                throw new XmlPullParserException(testSource.getAttribute("encoding") + " is not a valid encoding.");
            } catch (UnsupportedCharsetException uce) {
                throw new XmlPullParserException(testSource.getAttribute("encoding") + " is not a supported encoding.");
            }
        }
    }

    /**
     * Adds the test-resource element <code>resource</code> to the list of test-resources in the
     * <code>descriptor</code> and sets its <code>TemplateDescriptor</code> to
     * <i>filtered</i> if the attribute <code>filtered</code> was not
     * specified or its value is <code>&quot;true&quot;</code>, or <code>false</code>
     * if its value is <code>&quot;false&quot;</code>, and the encoding specified
     * in the <code>encoding</code> attribute or the Java virtual machine's default if
     * it is not defined. If the <code>resource</code> is a property file (ends in
     * <code>.properties</code>) its encoding will be set to <code>iso-8859-1</code>
     * even if some other encoding is specified in the attribute.
     *
     * @param testResource a <code>&lt;resource&gt;</code> element from the <code>&lt;testResources&gt;</code>
     * @param descriptor   the <code>ArchetypeDescriptor</code> to add the test-resource template to
     * @throws XmlPullParserException if the encoding specified is not valid or supported or if the
     *                                value of the attribute <code>filtered</code> is no valid
     */
    private static void addTestResourceToDescriptor(Xpp3Dom testResource, ArchetypeDescriptor descriptor)
            throws XmlPullParserException {
        descriptor.addTestResource(testResource.getValue());

        if (testResource.getAttribute("filtered") != null) {
            TemplateDescriptor testResourceDesc = descriptor.getTestResourceDescriptor(testResource.getValue());

            try {
                testResourceDesc.setFiltered(getValueFilteredAttribute(testResource.getAttribute("filtered")));
            } catch (IllegalArgumentException iae) {
                throw new XmlPullParserException(iae.getMessage());
            }
        }

        if (testResource.getAttribute("encoding") != null) {
            TemplateDescriptor testResourceDesc = descriptor.getTestResourceDescriptor(testResource.getValue());

            try {
                testResourceDesc.setEncoding(testResource.getAttribute("encoding"));

            } catch (IllegalCharsetNameException icne) {
                throw new XmlPullParserException(testResource.getAttribute("encoding") + " is not a valid encoding.");
            } catch (UnsupportedCharsetException uce) {
                throw new XmlPullParserException(
                        testResource.getAttribute("encoding") + " is not a supported encoding.");
            }
        }

        if (testResource.getValue().endsWith(".properties")) {
            TemplateDescriptor testResourceDesc = descriptor.getTestResourceDescriptor(testResource.getValue());

            testResourceDesc.setEncoding("iso-8859-1");
        }
    }

    /**
     * Adds the site-resource element <code>resource</code> to the list of site-resources in the
     * <code>descriptor</code> and sets its <code>TemplateDescriptor</code> to
     * <i>filtered</i> if the attribute <code>filtered</code> was not
     * specified or its value is <code>&quot;true&quot;</code>, or <code>false</code>
     * if its value is <code>&quot;false&quot;</code>, and the encoding specified
     * in the <code>encoding</code> attribute or the Java virtual machine's default if
     * it is not defined. If the <code>resource</code> is a property file (ends in
     * <code>.properties</code>) its encoding will be set to <code>iso-8859-1</code>
     * even if some other encoding is specified in the attribute.
     *
     * @param siteResource a <code>&lt;resource&gt;</code> element from the <code>&lt;siteResources&gt;</code>
     * @param descriptor   the <code>ArchetypeDescriptor</code> to add the site-resource template to
     * @throws XmlPullParserException if the encoding specified is not valid or supported or if the
     *                                value of the attribute <code>filtered</code> is no valid
     */
    private static void addSiteResourceToDescriptor(Xpp3Dom siteResource, ArchetypeDescriptor descriptor)
            throws XmlPullParserException {
        descriptor.addSiteResource(siteResource.getValue());

        if (siteResource.getAttribute("filtered") != null) {
            TemplateDescriptor siteResourceDesc = descriptor.getSiteResourceDescriptor(siteResource.getValue());

            try {
                siteResourceDesc.setFiltered(getValueFilteredAttribute(siteResource.getAttribute("filtered")));
            } catch (IllegalArgumentException iae) {
                throw new XmlPullParserException(iae.getMessage());
            }
        }
        if (siteResource.getAttribute("encoding") != null) {
            TemplateDescriptor siteResourceDesc = descriptor.getSiteResourceDescriptor(siteResource.getValue());

            try {
                siteResourceDesc.setEncoding(siteResource.getAttribute("encoding"));
            } catch (IllegalCharsetNameException icne) {
                throw new XmlPullParserException(siteResource.getAttribute("encoding") + " is not a valid encoding.");
            } catch (UnsupportedCharsetException uce) {
                throw new XmlPullParserException(
                        siteResource.getAttribute("encoding") + " is not a supported encoding.");
            }
        }
        if (siteResource.getValue().endsWith(".properties")) {
            TemplateDescriptor siteResourceDesc = descriptor.getSiteResourceDescriptor(siteResource.getValue());

            siteResourceDesc.setEncoding("iso-8859-1");
        }
    }

    private static boolean getValueFilteredAttribute(String str) throws IllegalArgumentException {
        boolean ret = false;

        if (str.equals("true")) {
            ret = true;
        } else if (str.equals("false")) {
            ret = false;
        } else {
            throw new IllegalArgumentException(str + " is not an accepted value for the attribute 'filtered'");
        }
        return ret;
    }
}
