QueryCatalog.java

/**
 * Copyright (c) 2017-2018, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * SPDX-License-Identifier: MPL-2.0
 */

package com.powsybl.triplestore.api;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
 */
public class QueryCatalog extends HashMap<String, String> {

    private InputStream resourceStream(String resource) {
        return getClass().getClassLoader().getResourceAsStream(resource);
    }

    public QueryCatalog(String resource) {
        this.resource = resource;
        load(resourceStream(resource), new ParsingContext());
    }

    public String resource() {
        return resource;
    }

    private void load(InputStream ir, ParsingContext context) {
        try (Stream<String> stream = new BufferedReader(
                new InputStreamReader(ir)).lines()) {
            stream.forEach(line -> parse(line, context));
        }
        context.leaveQuery();
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), resource);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof QueryCatalog q)) {
            return false;
        }
        return resource.equals(q.resource);
    }

    private void parse(String line, ParsingContext context) {
        if (queryDefinition(line)) {
            context.enterQuery(line);
        } else if (include(line)) {
            context.leaveQuery();
            load(resourceStream(includedResource(line)), context);
        } else if (comment(line)) {
            // Ignore the line
        } else {
            context.appendText(line);
        }
    }

    private static boolean queryDefinition(String line) {
        // A line with a query definition is a comment line
        // that contains the keyword for query definition
        if (!comment(line)) {
            return false;
        }
        return line.contains(QUERY_DEFINITION);
    }

    private static boolean comment(String line) {
        return line.trim().startsWith("#");
    }

    private static boolean include(String line) {
        return line.contains(INCLUDE);
    }

    private static String includedResource(String line) {
        int p = line.indexOf(INCLUDE);
        if (p <= 0) {
            throw new IllegalStateException("p should be > 0");
        }
        return line.substring(p + INCLUDE.length()).trim();
    }

    class ParsingContext {
        ParsingContext() {
            queryText = new StringBuilder(2048);
            queryName = null;
        }

        void enterQuery(String line) {
            leaveQuery();
            int p = line.indexOf(QUERY_DEFINITION);
            if (p <= 0) {
                throw new IllegalStateException("p should be > 0");
            }
            queryName = line.substring(p + QUERY_DEFINITION.length()).trim();
        }

        void leaveQuery() {
            if (queryName != null) {
                LOG.debug("loaded query [{}]", queryName);
                put(queryName, queryText.toString());
                queryName = null;
                queryText.setLength(0);
            }
        }

        void appendText(String line) {
            queryText.append(line);
            queryText.append(LINE_SEPARATOR);
        }

        private final StringBuilder queryText;
        private String queryName;
    }

    private final String resource;

    private static final String QUERY_DEFINITION = "query:";
    private static final String INCLUDE = "include:";
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    private static final Logger LOG = LoggerFactory.getLogger(QueryCatalog.class);
}