package de.is24.deadcode4j.analyzer;
import de.is24.deadcode4j.AnalysisContext;
import org.apache.commons.io.IOUtils;
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.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import static com.google.common.base.Preconditions.checkArgument;
import static de.is24.deadcode4j.Utils.isNotBlank;
/**
* Serves as a base class with which to analyze XML files.
*
* @since 1.2.0
*/
public abstract class XmlAnalyzer extends AnalyzerAdapter {
private final SAXParser parser;
private final String endOfFileName;
/**
* The constructor for an <code>XmlAnalyzer</code>.
*
* @param endOfFileName the file suffix used to determine if a file should be analyzed; this can be a mere file
* extension like <tt>.xml</tt> or a partial path like <tt>WEB-INF/web.xml</tt>
* @since 1.4
*/
protected XmlAnalyzer(@Nonnull String endOfFileName) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setFeature("http://xml.org/sax/features/namespaces", true);
this.parser = factory.newSAXParser();
} catch (Exception e) {
throw new RuntimeException("Failed to set up XML parser!", e);
}
checkArgument(isNotBlank(endOfFileName), "[endOfFileName] must be set!");
this.endOfFileName = endOfFileName;
}
@Override
public String toString() {
return super.toString() + " analyzing [" + endOfFileName + "] files";
}
@Override
public final void doAnalysis(@Nonnull AnalysisContext analysisContext, @Nonnull File file) {
if (file.getName().endsWith(endOfFileName)) {
logger.debug("Analyzing XML file [{}]...", file);
analyzeXmlFile(analysisContext, file);
}
}
/**
* This method is called to provide a <code>DefaultHandler</code> for each file being processed.
*
* @since 1.4
*/
@Nonnull
protected abstract DefaultHandler createHandlerFor(@Nonnull AnalysisContext analysisContext);
@SuppressWarnings("PMD.EmptyCatchBlock")
private void analyzeXmlFile(@Nonnull AnalysisContext analysisContext, @Nonnull File file) {
InputStream in = null;
try {
in = new FileInputStream(file);
parser.parse(in, createHandlerFor(analysisContext));
} catch (StopParsing command) {
// just do nothing
} catch (Exception e) {
throw new RuntimeException("Failed to parse [" + file + "]!", e);
} finally {
IOUtils.closeQuietly(in);
}
}
/**
* Used to indicate that XML parsing can be stopped.
*
* @since 1.2.0
*/
protected static final class StopParsing extends SAXException {
}
}