org.reusables.dbunit.autocomplete.AutoCompletionRules.java Source code

Java tutorial

Introduction

Here is the source code for org.reusables.dbunit.autocomplete.AutoCompletionRules.java

Source

/*
 * Copyright 2010-2012 the original author or authors.
 *
 * 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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */
package org.reusables.dbunit.autocomplete;

import org.reusables.dbunit.autocomplete.AutoCompletionColumn.ColumnType;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

/**
 * Auto completion rules.
 * 
 * @author marcel
 * @since 1.1
 **/
class AutoCompletionRules {
    private static final Map<URL, AutoCompletionRules> PARSED_RULES = new ConcurrentHashMap<URL, AutoCompletionRules>();

    private static final String GEN_KEYWORD_PREFIX = "${gen";
    private static final String GEN_KEYWORD_POSTFIX = "}";
    private static final String GEN_SEPARATOR = ":";

    private static final String ATTR_GEN_POSTFIX = "gen-postfix";
    private static final String ATTR_GEN_PREFIX = "gen-prefix";
    private static final String ATTR_GEN_SEPARATOR = "gen-separator";
    private static final String ATTR_NAME = "name";
    private static final String ATTR_MAX = "max";
    private static final String ATTR_MIN = "min";
    private static final String ATTR_OPTIONAL = "optional";
    private static final String ATTR_REFERENCE = "reference";
    private static final String ELEM_TABLE = "table";
    private static final String ELEM_RULES = "dataset-rules";

    private final Map<String, Map<String, AutoCompletionColumn>> tables = new HashMap<String, Map<String, AutoCompletionColumn>>();
    private String genPrefix = GEN_KEYWORD_PREFIX;
    private String genPostfix = GEN_KEYWORD_POSTFIX;
    private String genPrefixPlusSeparator = GEN_KEYWORD_PREFIX + GEN_SEPARATOR;

    private AutoCompletionRules() {
        // Empty
    }

    /**
     * Parses the rules from the given URL.
     * @param rulesFileUrl URL for the file to parse.
     * @return The parsed {@link AutoCompletionRules}.
     */
    static AutoCompletionRules parseRules(final URL rulesFileUrl) {
        final AutoCompletionRules existingRules = PARSED_RULES.get(rulesFileUrl);

        if (existingRules != null) {
            return existingRules;
        }

        final AutoCompletionRules rules = new AutoCompletionRules();
        rules.parse(rulesFileUrl);
        PARSED_RULES.put(rulesFileUrl, rules);
        return rules;
    }

    /**
     * @param tableName The table to get the columns for.
     * @return The column auto completion data for the table.
     */
    Collection<AutoCompletionColumn> getColumns(final String tableName) {
        final Map<String, AutoCompletionColumn> table = this.tables.get(tableName.toLowerCase());

        if (table != null) {
            return table.values();
        }

        return Collections.emptySet();
    }

    /**
     * @return The prefix for the keyword for forcing generation of optional columns and forcing the id for the generated referenced row.
     * @since 1.2.2
     */
    String getGenPrefix() {
        return this.genPrefix;
    }

    /**
     * @return The postfix for the keyword for forcing generation of optional columns and forcing the id for the generated referenced row.
     * @since 1.2.2
     */
    String getGenPostfix() {
        return this.genPostfix;
    }

    /**
     * @return The separator for the keyword for forcing the id for the generated referenced row.
     * @since 1.2.2
     */
    String getGenPrefixPlusSeparator() {
        return this.genPrefixPlusSeparator;
    }

    /**
     * @param tableName Table to find the pk column for.
     * @return The primary key column for the given table.
     */
    public AutoCompletionColumn findPkColumn(final String tableName) {
        for (final AutoCompletionColumn column : this.tables.get(tableName.toLowerCase()).values()) {
            if (column.getType().equals(ColumnType.PK)) {
                return column;
            }
        }
        return null;
    }

    private void parse(final URL rulesFileUrl) {
        InputStream input = null;
        XMLStreamReader parser = null;

        try {
            final XMLInputFactory factory = XMLInputFactory.newInstance();

            input = rulesFileUrl.openStream();
            parser = factory.createXMLStreamReader(input);

            for (int event = parser.next(); event != XMLStreamConstants.END_DOCUMENT; event = parser.next()) {
                if (event == XMLStreamConstants.START_ELEMENT && ELEM_RULES.equals(parser.getLocalName())) {
                    parseRules(parser);
                } else if (event == XMLStreamConstants.START_ELEMENT && ELEM_TABLE.equals(parser.getLocalName())) {
                    parseTable(parser);
                }
            }
        } catch (final XMLStreamException e) {
            throw new DbUnitAutoCompletionException("Error parsing xml stream.", e);
        } catch (final IOException e) {
            throw new DbUnitAutoCompletionException("Error reading stream.", e);
        } finally {
            IOUtils.closeQuietly(input);
            closeParser(parser);
        }
    }

    private void parseRules(final XMLStreamReader parser) {
        this.genPrefix = parseString(parser, ATTR_GEN_PREFIX, this.genPrefix);
        Validate.isTrue(StringUtils.isNotBlank(this.genPrefix), "Attribute cannot be blank: " + ATTR_GEN_PREFIX);

        this.genPostfix = parseString(parser, ATTR_GEN_POSTFIX, this.genPostfix);

        final String separator = parseString(parser, ATTR_GEN_SEPARATOR, GEN_SEPARATOR);
        this.genPrefixPlusSeparator = this.genPrefix + separator;
    }

    private void parseTable(final XMLStreamReader parser) throws XMLStreamException {
        final String name = getName(parser);
        final Map<String, AutoCompletionColumn> columns = addTable(name);
        AutoCompletionColumn currentColumn = null;

        for (int event = parser.next(); event != XMLStreamConstants.END_DOCUMENT; event = parser.next()) {
            if (event == XMLStreamConstants.START_ELEMENT) {
                currentColumn = parseColumn(parser);
                columns.put(currentColumn.getName().toLowerCase(), currentColumn);
            } else if (event == XMLStreamConstants.CHARACTERS || event == XMLStreamConstants.CDATA) {
                parseColumnValue(parser, currentColumn);
            } else if (event == XMLStreamConstants.END_ELEMENT) {
                currentColumn = null;
                if (ELEM_TABLE.equals(parser.getLocalName())) {
                    return;
                }
            }
        }
    }

    private Map<String, AutoCompletionColumn> addTable(final String tableName) {
        final Map<String, AutoCompletionColumn> columns = new HashMap<String, AutoCompletionColumn>();

        if (this.tables.put(tableName.toLowerCase(), columns) != null) {
            throw new IllegalArgumentException("Duplicate definition for table: " + tableName);
        }

        return columns;
    }

    private AutoCompletionColumn parseColumn(final XMLStreamReader parser) {
        final ColumnType type = ColumnType.valueOf(parser.getLocalName().toUpperCase());
        final AutoCompletionColumn column = new AutoCompletionColumn(type, getName(parser));
        column.setMin(parseNumber(parser, ATTR_MIN));
        column.setMax(parseNumber(parser, ATTR_MAX));
        column.setOptional(parseBoolean(parser, ATTR_OPTIONAL));
        column.setReference(parser.getAttributeValue(null, ATTR_REFERENCE));
        return column;
    }

    private void parseColumnValue(final XMLStreamReader parser, final AutoCompletionColumn currentColumn) {
        if (currentColumn != null) {
            currentColumn.setValue(parser.getText());
        }
    }

    private void closeParser(final XMLStreamReader parser) {
        try {
            if (parser != null) {
                parser.close();
            }
        } catch (final XMLStreamException e) {
            throw new DbUnitAutoCompletionException("Error closing parser", e);
        }
    }

    private String getName(final XMLStreamReader parser) {
        final String name = parser.getAttributeValue(null, ATTR_NAME);

        if (name == null || name.length() == 0) {
            throw new IllegalArgumentException("Missing 'name' attribute.");
        }

        return name;
    }

    private String parseString(final XMLStreamReader parser, final String attributeName,
            final String defaultValue) {
        final String value = parser.getAttributeValue(null, attributeName);
        return value != null ? value : defaultValue;
    }

    private Integer parseNumber(final XMLStreamReader parser, final String attributeName) {
        final String value = parser.getAttributeValue(null, attributeName);

        if (value != null) {
            return Integer.parseInt(value);
        }

        return null;
    }

    private boolean parseBoolean(final XMLStreamReader parser, final String attributeName) {
        final String value = parser.getAttributeValue(null, attributeName);

        if (value != null) {
            return Boolean.parseBoolean(value);
        }

        return false;
    }
}