class ConfigUtil {

    public Config buildConfig(HashMap<QName,ParameterValue> inputParameters) throws SaxonApiException, InvalidSyntaxException {
        try {
            Processor processor = new Processor(saxonConfig);
            if(!skipSchemaValidation) {
                try {
                    System.setProperty("javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema/v1.1","org.apache.xerces.jaxp.validation.XMLSchema11Factory");
                    SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema/v1.1");
                    Source schemaSource = saxonConfig.getURIResolver().resolve("cp:/fr/efl/chaine/xslt/schemas/gaulois-pipe_config.xsd", null);
                    Schema schema = schemaFactory.newSchema(schemaSource);
                    SchemaValidationErrorListener errListener = new SchemaValidationErrorListener();
                    Validator validator = schema.newValidator();
                    validator.setErrorHandler(errListener);
                    SAXSource saxSource = new SAXSource(
                            __isConfigUriTrueURI ? new InputSource(new URL(configUri).openStream()) :
                                    new InputSource(new FileInputStream(new File(configUri)))
                    );
                    validator.validate(saxSource);
                    if(errListener.hasErrors()) {
                        throw new InvalidSyntaxException(configUri+" does not respect gaulois-pipe schema");
                    }
                } catch(SAXException | TransformerException | IOException ex) {
                    LOGGER.error("while verifying schema conformity",ex);
                } catch(Error er) {
                    LOGGER.error("java.protocol.handler.pkgs="+System.getProperty("java.protocol.handler.pkgs"));
                    LOGGER.error("while parsing config",er);
                    throw er;
                }
            }
            if(LOGGER.isDebugEnabled() && __isConfigUriTrueURI) {
                // check if resolver can resolve URI
                LOGGER.debug("trying to resolve "+configUri);
                Source source = uriResolver.resolve(configUri, null);
                if(source==null) {
                    LOGGER.error(configUri+" can not be resolved. Please try again, setting org.xmlresolver logger to DEBUG");
                }
            }
            XdmNode configRoot = 
                    __isConfigUriTrueURI ? 
                    processor.newDocumentBuilder().build(uriResolver.resolve(configUri,null)) :
                    processor.newDocumentBuilder().build(new File(configUri));
            XPathCompiler xc = processor.newXPathCompiler();
            xc.declareNamespace("cfg", Config.NS);
            XPathSelector xs = xc.compile("/*").load();
            xs.setContextItem(configRoot);
            XdmNode root = (XdmNode)xs.evaluateSingle();
            if(CONFIG_EL.equals(root.getNodeName())) {
                Config config = new Config(root);
                // namespaces
                XdmSequenceIterator it = root.axisIterator(Axis.CHILD, Namespaces.QNAME);
                Namespaces namespaces = new Namespaces();
                while(it.hasNext()) {
                    XdmNode ns = (XdmNode)it.next();
                    XdmSequenceIterator itm = ns.axisIterator(Axis.CHILD, Namespaces.QN_MAPPING);
                    while(itm.hasNext()) {
                        XdmNode node = (XdmNode)itm.next();
                        String prefix = node.getAttributeValue(Namespaces.ATTR_PREFIX);
                        String uri = node.getAttributeValue(Namespaces.ATTR_URI);
                        namespaces.getMappings().put(prefix, uri);
                    }
                }
                config.setNamespaces(namespaces);
                it.close();
                // grammars issue #40
                XPathSelector gs = xc.compile("cfg:grammars/cfg:schema/@href").load();
                gs.setContextItem(root);
                it=gs.evaluate().iterator();
                List<String> grammars = new ArrayList<>();
                while(it.hasNext()) {
                    String href = it.next().getStringValue();
                    grammars.add(href);
                }
                it.close();
                config.setSchemaLocations(grammars);
                // params
//                config.getParams().putAll(inputParameters);
                HashMap<QName, ParameterValue> configParameters = new HashMap<>();
                it = root.axisIterator(Axis.CHILD, Config.PARAMS_CHILD);
                while(it.hasNext()) {
                    XdmNode params = (XdmNode)it.next();
                    XdmSequenceIterator itp = params.axisIterator(Axis.CHILD, new QName(Config.NS, "param"));
                    while(itp.hasNext()) {
                        //config.addParameter(buildParameter((XdmNode)itp.next(),inputParameters));
                        ParameterValue pv = buildParameter((XdmNode)itp.next(),inputParameters);
                        configParameters.put(pv.getKey(), pv);
                    }
                }
                config.getParams().putAll(ParametersMerger.merge(inputParameters, configParameters));
                // pipe
                config.setPipe(buildPipe((XdmNode)(root.axisIterator(Axis.CHILD, Pipe.QNAME).next()),config.getParams()));
                // sources
                XdmSequenceIterator sourceIterator = root.axisIterator(Axis.CHILD, Sources.QNAME);
                if(sourceIterator.hasNext()) {
                    config.setSources(buildSources((XdmNode)(sourceIterator.next()), config.getParams()));
                }
                // output
                // pour compatibilité ascendante, si on a un output sous la config, on essaie de le mettre sur le pipe
                // possible uniquement si le pipe est rectiligne
                XdmSequenceIterator outputIt = root.axisIterator(Axis.CHILD, Output.QNAME);
                if(outputIt.hasNext()) {
                    LOGGER.warn("Defining <output/> in config is now deprecated - but still works. You should define it in <pipe/>");
                    if(config.getPipe().getOutput()==null) {
                        config.getPipe().setOutput(buildOutput((XdmNode)(outputIt.next()), config.getParams()));
                    } else {
                        throw new InvalidSyntaxException("Using output outside of pipe is deprecated but supported, only if pipe has no output defined");
                    }
                }
                return config;
            } else {
                throw new InvalidSyntaxException("The file "+configUri+" does not respect schema saxon-pipe_config.xsd");
            }
        } catch(TransformerException ex) {
            throw new InvalidSyntaxException(configUri+" can not be read.");
        }
    }

    public  ConfigUtil(Configuration saxonConfig, URIResolver uriResolver, String configUri);
    public  ConfigUtil(Configuration saxonConfig, URIResolver uriResolver, String configUri, final boolean skipSchemaValidation, final String currentDir);

}