import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public final class JdkSaxLint {
  public static void main(String[] args) {
    if (args.length != 1) {
      System.err.println("usage: jdk-sax-lint FILE");
      System.exit(2);
    }

    try {
      SAXParserFactory factory = SAXParserFactory.newDefaultNSInstance();
      factory.setXIncludeAware(false);
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
      factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
      factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
      factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

      XMLReader reader = factory.newSAXParser().getXMLReader();
      reader.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
      StrictHandler handler = new StrictHandler();
      reader.setContentHandler(handler);
      reader.setErrorHandler(handler);
      reader.setEntityResolver((publicId, systemId) -> {
        throw new SAXException("External entities are disabled");
      });

      Path path = Path.of(args[0]);
      try (InputStream stream = Files.newInputStream(path)) {
        InputSource input = new InputSource(stream);
        input.setSystemId(path.toUri().toString());
        reader.parse(input);
      }
    } catch (Exception error) {
      System.err.println(error.getClass().getSimpleName() + ": " + error.getMessage());
      System.exit(1);
    }
  }

  private static final class StrictHandler extends DefaultHandler {
    @Override
    public void error(SAXParseException error) throws SAXException {
      throw error;
    }

    @Override
    public void fatalError(SAXParseException error) throws SAXException {
      throw error;
    }
  }
}
