summaryrefslogtreecommitdiffstats
path: root/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java
blob: 16469bb13ae48ab27d7416754bb4342cc3c2b72e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.application.provider;

import com.thaiopensource.util.PropertyMap;
import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.ValidationDriver;
import com.thaiopensource.validate.rng.CompactSchemaReader;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.io.reader.NamedReader;
import com.yahoo.yolean.Exceptions;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.logging.Level;

/**
 * Validates xml files against a schema.
 *
 * @author Tony Vaagenes
 */
public class SchemaValidator {

    private final CustomErrorHandler errorHandler = new CustomErrorHandler();
    private final ValidationDriver driver;
    private final DeployLogger deployLogger;

    /**
     * Initializes the validator by using the given file as schema file
     *
     * @param schemaFile schema file
     * @throws IOException if it is not possible to read schema files
     */
    SchemaValidator(File schemaFile, DeployLogger deployLogger) throws IOException, SAXException {
        this.deployLogger = deployLogger;
        this.driver = new ValidationDriver(PropertyMap.EMPTY, instanceProperties(), CompactSchemaReader.getInstance());
        driver.loadSchema(ValidationDriver.fileInputSource(schemaFile));
    }

    public void validate(File file) throws IOException {
        validate(file, file.getName());
    }

    public void validate(File file, String fileName) throws IOException {
        validate(ValidationDriver.fileInputSource(file), fileName);
    }

    public void validate(Reader reader) throws IOException {
        validate(new InputSource(reader), null);
    }

    public void validate(NamedReader reader) throws IOException {
        validate(new InputSource(reader), reader.getName());
    }

    public void validate(InputSource inputSource, String fileName)  throws IOException {
        errorHandler.fileName = (fileName == null ? " input" : fileName);
        try {
            if ( ! driver.validate(inputSource)) {
                // Shouldn't happen, error handler should have thrown
                throw new RuntimeException("Aborting due to earlier XML errors.");
            }
        } catch (SAXException e) {
            // This should never happen, as it is handled by the ErrorHandler
            // installed for the driver.
            throw new IllegalArgumentException(
                    "XML error in " + (fileName == null ? " input" : fileName) + ": " + Exceptions.toMessageString(e));
        }
    }

    private PropertyMap instanceProperties() {
        PropertyMapBuilder builder = new PropertyMapBuilder();
        builder.put(ValidateProperty.ERROR_HANDLER, errorHandler);
        return builder.toPropertyMap();
    }

    private class CustomErrorHandler implements ErrorHandler {
        volatile String fileName;

        public void warning(SAXParseException e) {
            deployLogger.log(Level.WARNING, message(e));
        }

        public void error(SAXParseException e) {
            throw new IllegalArgumentException(message(e));
        }

        public void fatalError(SAXParseException e) {
            throw new IllegalArgumentException(message(e));
        }

        private String message(SAXParseException e) {
            return "XML error in " + fileName + ": " +
                    Exceptions.toMessageString(e)
                    + " [" + e.getLineNumber() + ":" + e.getColumnNumber() + "]";
        }
    }

}