com.mingo.parser.xml.dom.QuerySetParser.java Source code

Java tutorial

Introduction

Here is the source code for com.mingo.parser.xml.dom.QuerySetParser.java

Source

package com.mingo.parser.xml.dom;

import static com.mingo.parser.xml.dom.DocumentBuilderFactoryCreator.createDocumentBuilderFactory;
import static com.mingo.parser.xml.dom.DomUtil.getAttributeBoolean;
import static com.mingo.parser.xml.dom.DomUtil.getAttributeInt;
import static com.mingo.parser.xml.dom.DomUtil.getAttributeString;
import static com.mingo.parser.xml.dom.DomUtil.getAttributes;
import static com.mingo.parser.xml.dom.DomUtil.getFirstNecessaryTagOccurrence;
import static com.mingo.parser.xml.dom.DomUtil.isNotEmpty;
import static com.mingo.query.util.QueryUtils.validate;
import static com.mingo.query.util.QueryUtils.wrap;
import com.google.common.collect.Sets;
import com.mingo.exceptions.ParserException;
import com.mingo.parser.Parser;
import com.mingo.query.Query;
import com.mingo.query.QueryCase;
import com.mingo.query.QueryFragment;
import com.mingo.query.QuerySet;
import com.mingo.query.QueryType;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;

import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Copyright 2012-2013 The Mingo Team
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p/>
 * This class is implementation of {@link Parser} interface for parsing QuerySet xml.
 */
public class QuerySetParser implements Parser<QuerySet> {

    private DocumentBuilderFactory documentBuilderFactory;

    /* default handler */
    private ErrorHandler parseErrorHandler = new ParseErrorHandler();

    private static final Logger LOGGER = LoggerFactory.getLogger(QuerySetParser.class);

    private static final String ID = "id";
    private static final String CONDITION = "condition";
    private static final String QUERY_SET_TAG = "querySet";
    private static final String CONFIG_TAG = "config";
    private static final String QUERY_TAG = "query";
    // private static final String CASE_NAME = "name"; // optional
    private static final String CASE_TAG = "case";
    private static final String QUERY_FRAGMENT = "queryFragment";
    private static final String FRAGMENT = "fragment";
    private static final String FRAGMENT_REF_ATTR = "ref";
    private static final String CASE_PRIORITY_ATTR = "priority";
    private static final String COLLECTION_NAME_ATTR = "collectionName";
    private static final String CONVERTER_CLASS_ATTR = "converter-class";
    private static final String CONVERTER_METHOD_ATTR = "converter-method";
    private static final String CONVERTER_INHERIT_ATTR = "converter-inherit";
    private static final String DB_NAME = "dbName";
    private static final String TYPE_ATTR = "type";

    private static final String INVALID_QUERY_ERROR_MSG = "invalid query with id: {}. Query: {}";

    /**
     * Constructor with parameters.
     *
     * @param parserConfiguration parser configuration
     * @param parseErrorHandler   parser error handler
     * @throws ParserConfigurationException {@link ParserConfigurationException}
     */
    public QuerySetParser(ParserConfiguration parserConfiguration, ErrorHandler parseErrorHandler)
            throws ParserConfigurationException {
        this.documentBuilderFactory = createDocumentBuilderFactory(parserConfiguration);
        this.parseErrorHandler = parseErrorHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public QuerySet parse(InputStream xml) throws ParserException {
        LOGGER.debug("QuerySetParser:: START PARSING");
        QuerySet querySet;
        try {
            DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
            builder.setErrorHandler(parseErrorHandler);
            Document document = builder.parse(new InputSource(xml));
            Element root = document.getDocumentElement();
            querySet = new QuerySet();
            parseConfigTag(root, querySet);
            parseQueryFragments(root, querySet);
            parseQueries(root, querySet);
        } catch (Exception e) {
            throw new ParserException(e);
        }
        return querySet;
    }

    /**
     * parse <config/> tag and add parsed information to querySet.
     *
     * @param element  element of XML document
     * @param querySet {@link QuerySet}
     */
    private void parseConfigTag(Element element, QuerySet querySet) throws ParserException {
        Validate.notNull(element, "element cannot be null");
        Validate.notNull(querySet, "query set cannot be null");
        Node configTag = getFirstNecessaryTagOccurrence(element, CONFIG_TAG);
        String collectionName = getAttributeString(configTag, COLLECTION_NAME_ATTR);
        Validate.notEmpty(collectionName, "collectionName cannot be null or empty");
        querySet.setCollectionName(collectionName);
        querySet.setDbName(getAttributeString(configTag, DB_NAME));
    }

    private void parseQueryFragments(Element root, QuerySet querySet) {
        Set<QueryFragment> queryFragments = Collections.emptySet();
        NodeList fragmentNodeList = root.getElementsByTagName(QUERY_FRAGMENT);
        if (isNotEmpty(fragmentNodeList)) {
            queryFragments = Sets.newHashSet();
            for (int i = 0; i < fragmentNodeList.getLength(); i++) {
                queryFragments.add(parseQueryFragment(fragmentNodeList.item(i)));
            }
        }
        querySet.setQueryFragments(queryFragments);
    }

    /**
     * Parse <queryFragment/> tag.
     *
     * @param fragmentNode node
     * @return {@link QueryCase}
     */
    private QueryFragment parseQueryFragment(Node fragmentNode) {
        String fragmentId = getAttributeString(fragmentNode, ID);
        String fragmentBody = processString(fragmentNode.getTextContent());
        return new QueryFragment(fragmentId, fragmentBody);
    }

    /**
     * Parse query sequence. XML document can have one or more query tags, also no query tags can be.
     *
     * @param root element XML document
     * @return set of {@link Query} elements
     */
    private void parseQueries(Element root, QuerySet querySet) throws ParserException {
        NodeList queryNodeList = root.getElementsByTagName(QUERY_TAG);
        if (isNotEmpty(queryNodeList)) {
            for (int i = 0; i < queryNodeList.getLength(); i++) {
                parseQueryTag(queryNodeList.item(i), querySet);
            }
        }
    }

    /**
     * Parse <query/> node.
     *
     * @param node node
     * @return {@link Query}
     */
    private void parseQueryTag(Node node, QuerySet querySet) throws ParserException {
        if (node == null || !QUERY_TAG.equals(node.getNodeName())) {
            return;
        }
        Map<String, String> attributes = getAttributes(node);
        StringBuilder queryBodyBuilder = new StringBuilder();
        Query query = new Query(attributes.get(ID));
        query.setQueryType(QueryType.getByName(attributes.get(TYPE_ATTR)));
        query.setConverter(attributes.get(CONVERTER_CLASS_ATTR));
        query.setConverterMethod(attributes.get(CONVERTER_METHOD_ATTR));
        if (node.hasChildNodes()) {
            NodeList childList = node.getChildNodes();
            for (int i = 0; i < childList.getLength(); i++) {
                Node child = childList.item(i);
                // parse query body
                parseBody(queryBodyBuilder, child, querySet);
                // parse <case> tag
                parseCaseTag(child, query, querySet);
            }
        }
        query.setBody(queryBodyBuilder.toString());

        if (!validate(wrap(query.getBody()))) {
            throw new ParserException(
                    MessageFormatter.format(INVALID_QUERY_ERROR_MSG, query.getId(), query).getMessage());
        }
        querySet.addQuery(query);
    }

    /**
     * Parse <case/> tag.
     *
     * @param node node
     * @return {@link QueryCase}
     */
    private void parseCaseTag(Node node, Query query, QuerySet querySet) throws ParserException {
        if (node == null || !CASE_TAG.equals(node.getNodeName())) {
            return;
        }
        QueryCase queryCase = new QueryCase();
        queryCase.setId(getAttributeString(node, ID));
        queryCase.setPriority(getAttributeInt(node, CASE_PRIORITY_ATTR));
        queryCase.setCondition(getAttributeString(node, CONDITION));
        QueryType type = QueryType.getByName(getAttributeString(node, TYPE_ATTR));
        queryCase.setQueryType(type != null ? type : query.getQueryType());
        boolean inheritConverter = getAttributeBoolean(node, CONVERTER_INHERIT_ATTR);
        if (!inheritConverter) {
            queryCase.setConverter(getAttributeString(node, CONVERTER_CLASS_ATTR));
            queryCase.setConverterMethod(getAttributeString(node, CONVERTER_METHOD_ATTR));
        } else {
            queryCase.setConverterMethod(query.getConverterMethod());
            queryCase.setConverter(query.getConverter());
        }

        // parse case body
        if (node.hasChildNodes()) {
            StringBuilder caseBodyBuilder = new StringBuilder();
            NodeList childList = node.getChildNodes();
            for (int i = 0; i < childList.getLength(); i++) {
                Node child = childList.item(i);
                parseBody(caseBodyBuilder, child, querySet);
            }

            queryCase.setBody(StringUtils.strip(caseBodyBuilder.toString()));
            if (!validate(wrap(queryCase.getBody()))) {
                throw new ParserException(MessageFormatter
                        .format(INVALID_QUERY_ERROR_MSG, queryCase.getId(), queryCase).getMessage());
            }
        }
        query.addQueryCase(queryCase);
    }

    private void parseBody(StringBuilder builder, Node node, QuerySet querySet) throws ParserException {
        if (node == null) {
            return;
        }
        if (node.getNodeType() == Node.TEXT_NODE) {
            String body = node.getNodeValue();
            if (StringUtils.isNotBlank(body)) {
                builder.append(processString(body));
            }
        } else if (FRAGMENT.equals(node.getNodeName())) {
            applyFragment(builder, getFragmentRef(node), querySet);
        }
    }

    private String getFragmentRef(Node child) {
        return getAttributeString(child, FRAGMENT_REF_ATTR);
    }

    private void applyFragment(StringBuilder body, String fRef, QuerySet querySet) throws ParserException {
        QueryFragment queryFragment = querySet.getQueryFragmentById(fRef);
        if (queryFragment != null) {
            body.append(processString(queryFragment.getBody()));
        } else {
            throw new ParserException("not found query fragment for ref: " + fRef);
        }
    }

    private String processString(String source) {
        if (StringUtils.isNotBlank(source)) {
            source = StringUtils.strip(source);
            source = source.replace("(?m)^[ \t]*\r?\n", "");
        }
        return source;
    }

}