package de.is24.deadcode4j;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.annotation.Nonnull;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.util.Collection;

import static com.google.common.collect.Lists.newArrayList;

/**
 * Analyzes Spring XML files: lists the classes being referenced.
 *
 * @since 1.0.2
 */
public class SpringXmlAnalyzer implements Analyzer {
    private final SAXParser parser;
    private final DefaultHandler handler;
    private final Collection<String> referencedClasses = newArrayList();

    public SpringXmlAnalyzer() {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            this.parser = factory.newSAXParser();
        } catch (Exception e) {
            throw new RuntimeException("Failed to set up XML parser!", e);
        }
        this.handler = new DefaultHandler() {
            private boolean firstElement = true;

            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws StopParsing {
                if (firstElement && !"beans".equals(qName)) {
                    throw new StopParsing();
                } else {
                    firstElement = false;
                }
                if (!"bean".equals(qName)) {
                    return;
                }

                String className = attributes.getValue("class");
                if (className != null) {
                    referencedClasses.add(className);
                }
            }
        };
    }

    @Override
    public void doAnalysis(@Nonnull CodeContext codeContext, @Nonnull String fileName) {
        if (fileName.endsWith(".xml")) {
            analyzeXmlFile(codeContext, fileName);
        }
    }

    private void analyzeXmlFile(@Nonnull CodeContext codeContext, @Nonnull String file) {
        this.referencedClasses.clear();
        try {
            parser.parse(codeContext.getClassLoader().getResourceAsStream(file), handler);
        } catch (StopParsing command) {
            return;
        } catch (Exception e) {
            throw new RuntimeException("Failed to parse [" + file + "]!", e);
        }
        codeContext.addDependencies("_Spring_", this.referencedClasses);
    }

    /**
     * Used to indicate that XML parsing can be stopped.
     *
     * @since 1.0.2
     */
    private static class StopParsing extends SAXException {
    }

}

@@ -533,23 +533,19 @@
 e final 
-Default
+Xml
 Handler 
@@ -1044,23 +1044,19 @@
 r = new 
-Default
+Xml
 Handler(
@@ -1060,693 +1060,8 @@
 er()
- %7B%0A            private boolean firstElement = true;%0A%0A            @Override%0A            public void startElement(String uri, String localName, String qName, Attributes attributes) throws StopParsing %7B%0A                if (firstElement && !%22beans%22.equals(qName)) %7B%0A                    throw new StopParsing();%0A                %7D else %7B%0A                    firstElement = false;%0A                %7D%0A                if (!%22bean%22.equals(qName)) %7B%0A                    return;%0A                %7D%0A%0A                String className = attributes.getValue(%22class%22);%0A                if (className != null) %7B%0A                    referencedClasses.add(className);%0A                %7D%0A            %7D%0A        %7D
 ;%0A  
@@ -1403,24 +1403,54 @@
 es.clear();%0A
+        this.handler.reset();%0A
         try 
@@ -1902,16 +1902,16 @@
      */%0A
-
     priv
@@ -1969,10 +1969,760 @@
 %0A    %7D%0A%0A
+    private class XmlHandler extends DefaultHandler %7B%0A        private boolean firstElement = true;%0A%0A        @Override%0A        public void startElement(String uri, String localName, String qName, Attributes attributes) throws StopParsing %7B%0A            if (firstElement && !%22beans%22.equals(qName)) %7B%0A                throw new StopParsing();%0A            %7D else %7B%0A                firstElement = false;%0A            %7D%0A            if (!%22bean%22.equals(qName)) %7B%0A                return;%0A            %7D%0A%0A            String className = attributes.getValue(%22class%22);%0A            if (className != null) %7B%0A                referencedClasses.add(className);%0A            %7D%0A        %7D%0A%0A        public void reset() %7B%0A            this.firstElement = true;%0A        %7D%0A%0A    %7D%0A%0A
 %7D%0A

fix ability to recognize Spring XML files