public Document merge(XPathExpression expression,
                          List<String> excludedTags,
                          Set<String> files)
            throws ParserConfigurationException,
                   IOException,
                   SAXException,
                   XPathExpressionException
    {
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        docBuilderFactory.setIgnoringElementContentWhitespace(true);
        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

        docBuilder.setEntityResolver(new EntityResolver()
        {
            public InputSource resolveEntity(String publicId,
                                             String systemId)
                    throws SAXException, IOException
            {
                // System.out.println("Ignoring " + publicId + ", " + systemId);

                final InputStream schemaInputStream = getInputStreamForSchema(systemId);

                if (schemaInputStream != null)
                {
                    return new InputSource(schemaInputStream);
                }
                else
                {
                    System.out.println("WARNING: Could not resolve '" + systemId +  "' from classpath.");

                    return new InputSource("");
                }
            }

            private InputStream getInputStreamForSchema(String systemId)
            {
                return getClass().getClassLoader().getResourceAsStream(SCHEMA_PATHS.get(systemId));
            }
        });

        Document outputDocument = docBuilder.parse(new FileInputStream(getOutputFileName()));

        Node results = (Node) expression.evaluate(outputDocument, XPathConstants.NODE);
        if (results == null)
        {
            throw new IOException(outputFileName + ": the expression does not evaluate to a node!");
        }

        if (!files.isEmpty())
        {
            System.out.println("Merging web.xml files...");
        }

        for (String file : files)
        {
            InputStream is = null;
            JarFile jarFile = null;

            try
            {
                if (file.toLowerCase().endsWith(".war"))
                {
                    jarFile = new JarFile(file);
                    ZipEntry entry = jarFile.getEntry("WEB-INF/web.xml");

                    is = jarFile.getInputStream(entry);
                    System.out.println(" -> Applying " + file + "/WEB-INF/web.xml");
                }
                else
                {
                    is = new FileInputStream(file);
                    System.out.println(" -> Applying " + file);
                }

                performMerge(expression, excludedTags, docBuilder, outputDocument, results, is);
            }
            finally
            {
                if (jarFile != null)
                {
                    jarFile.close();
                }

                if (is != null)
                {
                    is.close();
                }
            }
        }

        return outputDocument;
    }