org.apache.ddlutils.io.DataReaderKeys.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ddlutils.io.DataReaderKeys.java

Source

package org.apache.ddlutils.io;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ddlutils.io.converters.SqlTypeConverter;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.Table;
import org.xml.sax.InputSource;

/**
 * Reads data XML into dyna beans matching a specified database model. Note that
 * the data sink won't be started or ended by the data reader, this has to be done
 * in the code that uses the data reader. 
 * 
 * @version $Revision: $
 */
public class DataReaderKeys {
    /** Our log. */
    private final Log _log = LogFactory.getLog(DataReaderKeys.class);

    /** The database model. */
    private Database _model;
    /** The object to receive the read beans. */
    private DataSink _sink;
    /** The converters. */
    private ConverterConfiguration _converterConf = new ConverterConfiguration();
    /** Whether to be case sensitive or not. */
    private boolean _caseSensitive = false;

    //map of the keys and values for the tables
    public HashMap keymap = new HashMap();

    /**
     * Returns the converter configuration of this data reader.
     * 
     * @return The converter configuration
     */
    public ConverterConfiguration getConverterConfiguration() {
        return _converterConf;
    }

    /**
     * Returns the database model.
     *
     * @return The model
     */
    public Database getModel() {
        return _model;
    }

    /**
     * Sets the database model.
     *
     * @param model The model
     */
    public void setModel(Database model) {
        _model = model;
    }

    /**
     * Returns the data sink.
     *
     * @return The sink
     */
    public DataSink getSink() {
        return _sink;
    }

    /**
     * Sets the data sink.
     *
     * @param sink The sink
     */
    public void setSink(DataSink sink) {
        _sink = sink;
    }

    /**
     * Determines whether this rules object matches case sensitively.
     *
     * @return <code>true</code> if the case of the pattern matters
     */
    public boolean isCaseSensitive() {
        return _caseSensitive;
    }

    /**
     * Specifies whether this rules object shall match case sensitively.
     *
     * @param beCaseSensitive <code>true</code> if the case of the pattern shall matter
     */
    public void setCaseSensitive(boolean beCaseSensitive) {
        _caseSensitive = beCaseSensitive;
    }

    /**
     * Creates a new, initialized XML input factory object.
     * 
     * @return The factory object
     */
    private XMLInputFactory getXMLInputFactory() {
        XMLInputFactory factory = XMLInputFactory.newInstance();

        factory.setProperty("javax.xml.stream.isCoalescing", Boolean.TRUE);
        factory.setProperty("javax.xml.stream.isNamespaceAware", Boolean.FALSE);
        return factory;
    }

    /**
     * Reads the data contained in the specified file.
     * 
     * @param filename The data file name
     */
    public void read(String filename) throws DdlUtilsXMLException {
        try {
            read(new FileReader(filename));
        } catch (IOException ex) {
            throw new DdlUtilsXMLException(ex);
        }
    }

    /**
     * Reads the data contained in the specified file.
     * 
     * @param file The data file
     */
    public void read(File file) throws DdlUtilsXMLException {
        try {
            read(new FileReader(file));
        } catch (IOException ex) {
            throw new DdlUtilsXMLException(ex);
        }
    }

    /**
     * Reads the data given by the reader.
     * 
     * @param reader The reader that returns the data XML
     */
    public void read(Reader reader) throws DdlUtilsXMLException {
        try {
            read(getXMLInputFactory().createXMLStreamReader(reader));
        } catch (XMLStreamException ex) {
            throw new DdlUtilsXMLException(ex);
        }
    }

    /**
     * Reads the data given by the input stream.
     * 
     * @param input The input stream that returns the data XML
     */
    public void read(InputStream input) throws DdlUtilsXMLException {
        try {
            read(getXMLInputFactory().createXMLStreamReader(input));
        } catch (XMLStreamException ex) {
            throw new DdlUtilsXMLException(ex);
        }
    }

    /**
     * Reads the data from the given input source.
     *
     * @param source The input source
     */
    public void read(InputSource source) throws DdlUtilsXMLException {
        read(source.getCharacterStream());
    }

    /**
     * Reads the data from the given XML stream reader.
     * 
     * @param xmlReader The reader
     */
    private void read(XMLStreamReader xmlReader) throws DdlUtilsXMLException {
        try {
            while (xmlReader.getEventType() != XMLStreamReader.START_ELEMENT) {
                if (xmlReader.next() == XMLStreamReader.END_DOCUMENT) {
                    return;
                }
            }
            readDocument(xmlReader);
        } catch (XMLStreamException ex) {
            throw new DdlUtilsXMLException(ex);
        }
    }

    // TODO: add debug level logging (or trace ?)

    private void readDocument(XMLStreamReader xmlReader) throws XMLStreamException, DdlUtilsXMLException {
        // we ignore the top-level tag since we don't know about its name
        int eventType = XMLStreamReader.START_ELEMENT;

        while (eventType != XMLStreamReader.END_ELEMENT) {
            eventType = xmlReader.next();
            if (eventType == XMLStreamReader.START_ELEMENT) {
                Identity id = readBean(xmlReader);
                if (id != null) {
                    //check to see if keymap has the table already
                    Set keyset = (Set) keymap.get(id.getTable().getName());
                    if (keyset != null) {
                        keyset.add(id);
                    } else { //add the table
                        Set newset = new HashSet();
                        newset.add(id);
                        keymap.put(id.getTable().getName(), newset);
                    }
                }
            }
        }
    }

    private Identity readBean(XMLStreamReader xmlReader) throws XMLStreamException, DdlUtilsXMLException {
        QName elemQName = xmlReader.getName();
        Table table = _model.findTable(elemQName.getLocalPart(), isCaseSensitive());

        if (table == null) {
            readOverElement(xmlReader);
            return null;
        }

        DynaBean bean = _model.createDynaBeanFor(table);
        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++) {
            QName attrQName = xmlReader.getAttributeName(idx);
            Column column = table.findColumn(attrQName.getLocalPart(), isCaseSensitive());

            if (column != null) {
                setColumnValue(bean, table, column, xmlReader.getAttributeValue(idx));
            }
        }
        readColumnSubElements(xmlReader, bean, table);
        //getSink().addBean(bean);
        //--get keys here
        Identity id = buildIdentityFromPKs(table, bean);
        //Hashmap keyval = new Hashmap();
        //keyval.put(id.,id);
        consumeRestOfElement(xmlReader);
        return id;
    }

    private void readColumnSubElements(XMLStreamReader xmlReader, DynaBean bean, Table table)
            throws XMLStreamException, DdlUtilsXMLException {
        int eventType = XMLStreamReader.START_ELEMENT;

        while (eventType != XMLStreamReader.END_ELEMENT) {
            eventType = xmlReader.next();
            if (eventType == XMLStreamReader.START_ELEMENT) {
                readColumnSubElement(xmlReader, bean, table);
            }
        }
    }

    private void readColumnSubElement(XMLStreamReader xmlReader, DynaBean bean, Table table)
            throws XMLStreamException, DdlUtilsXMLException {
        QName elemQName = xmlReader.getName();
        boolean usesBase64 = false;

        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++) {
            QName attrQName = xmlReader.getAttributeName(idx);

            if (DatabaseIO.BASE64_ATTR_NAME.equals(attrQName.getLocalPart())
                    && "true".equalsIgnoreCase(xmlReader.getAttributeValue(idx))) {
                usesBase64 = true;
                break;
            }
        }

        Column column = table.findColumn(elemQName.getLocalPart(), isCaseSensitive());

        if (column == null) {
            _log.warn("Data XML contains an element " + elemQName + " at location " + xmlReader.getLocation()
                    + " but there is no column defined in table " + table.getName()
                    + " with this name. This element will be ignored.");
        } else {
            String value = xmlReader.getElementText();

            if (value != null) {
                value = value.trim();

                if (usesBase64) {
                    value = new String(Base64.decodeBase64(value.getBytes()));
                }
                setColumnValue(bean, table, column, value);
            }
        }
        consumeRestOfElement(xmlReader);
    }

    private void setColumnValue(DynaBean bean, Table table, Column column, String value)
            throws DdlUtilsXMLException {
        SqlTypeConverter converter = _converterConf.getRegisteredConverter(table, column);
        Object propValue = (converter != null ? converter.convertFromString(value, column.getTypeCode()) : value);

        try {
            PropertyUtils.setProperty(bean, column.getName(), propValue);
        } catch (NoSuchMethodException ex) {
            throw new DdlUtilsXMLException("Undefined column " + column.getName());
        } catch (IllegalAccessException ex) {
            throw new DdlUtilsXMLException("Could not set bean property for column " + column.getName(), ex);
        } catch (InvocationTargetException ex) {
            throw new DdlUtilsXMLException("Could not set bean property for column " + column.getName(), ex);
        }
    }

    // TODO: move these two into a helper class:

    /**
     * Reads over the current element. This assumes that the current XML stream event type is
     * START_ELEMENT.
     *  
     * @param reader The xml reader
     */
    private void readOverElement(XMLStreamReader reader) throws XMLStreamException {
        int depth = 1;

        while (depth > 0) {
            int eventType = reader.next();

            if (eventType == XMLStreamReader.START_ELEMENT) {
                depth++;
            } else if (eventType == XMLStreamReader.END_ELEMENT) {
                depth--;
            }
        }
    }

    /**
     * Consumes the rest of the current element. This assumes that the current XML stream
     * event type is not START_ELEMENT.
     * 
     * @param reader The xml reader
     */
    private void consumeRestOfElement(XMLStreamReader reader) throws XMLStreamException {
        int eventType = reader.getEventType();

        while ((eventType != XMLStreamReader.END_ELEMENT) && (eventType != XMLStreamReader.END_DOCUMENT)) {
            eventType = reader.next();
        }
    }

    private Identity buildIdentityFromPKs(Table table, DynaBean bean) {
        Identity identity = new Identity(table);
        Column[] pkColumns = table.getPrimaryKeyColumns();

        for (int idx = 0; idx < pkColumns.length; idx++) {
            identity.setColumnValue(pkColumns[idx].getName(), bean.get(pkColumns[idx].getName()));
        }
        return identity;
    }
}