Java tutorial
/* * 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; } }