/* * Copyright 2007 - 2017 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 net.sf.jailer.dbunit; import java.sql.Blob; import java.sql.Clob; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.HashMap; import java.util.Map; import javax.xml.transform.sax.TransformerHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import net.sf.jailer.ExecutionContext; import net.sf.jailer.configuration.Configuration; import net.sf.jailer.configuration.DBMS; import net.sf.jailer.database.Session.AbstractResultSetReader; import net.sf.jailer.database.Session.ResultSetReader; import net.sf.jailer.datamodel.Table; import net.sf.jailer.subsetting.TransformerFactory; import net.sf.jailer.util.Base64; /** * Reads a JDBC result set and writes the read rows into a * DbUnit flat XML dataset document. * * @see http://www.dbunit.org/ * @author Ralf Wisser */ public class FlatXMLTransformer extends AbstractResultSetReader { /** * Name of XML row element, or <code>null</code> if the table name can not * be converted into a valid XML tag identifier. */ private final String rowElementName; /** * To write the XML into. */ private final TransformerHandler transformerHandler; /** * Number of columns. */ private int columnCount; /** * Labels of columns. */ private String[] columnLabel = null; /** * Maps clear text SQL-types to {@link java.sql.Types}. */ private Map<Integer, Integer> typeCache = new HashMap<Integer, Integer>(); private final DBMS dbms; /** * Factory. */ public static class Factory implements TransformerFactory { private final TransformerHandler transformerHandler; private final DatabaseMetaData metaData; private final DBMS dbms; /** * The execution context. */ private final ExecutionContext executionContext; /** * Constructor. * * @param table * the table to read from * @param transformerHandler * to write the XML into * @param metaData * database meta data */ public Factory(TransformerHandler transformerHandler, DatabaseMetaData metaData, DBMS dbms, ExecutionContext executionContext) { this.executionContext = executionContext; this.transformerHandler = transformerHandler; this.metaData = metaData; this.dbms = dbms; } /** * Creates transformer (as {@link ResultSetReader} which * transforms rows of a given table into an external representation. * * @param table the table * @return a transformer */ @Override public ResultSetReader create(Table table) throws SQLException { return new FlatXMLTransformer(table, transformerHandler, metaData, dbms, executionContext); } } /** * The execution context. */ private final ExecutionContext executionContext; /** * Constructor. * * @param table * the table to read from * @param transformerHandler * to write the XML into * @param metaData * database meta data * @param commandLine2 */ private FlatXMLTransformer(Table table, TransformerHandler transformerHandler, DatabaseMetaData metaData, DBMS dbms, ExecutionContext executionContext) throws SQLException { this.executionContext = executionContext; this.transformerHandler = transformerHandler; this.rowElementName = qualifiedTableName(table); this.dbms = dbms; } /** * Gets qualified table name. * * @param t the table * @return qualified name of t */ private String qualifiedTableName(Table t) { String schema = t.getOriginalSchema(""); String mappedSchema = executionContext.getSchemaMapping().get(schema); if (mappedSchema != null) { schema = mappedSchema; } if (schema.length() == 0) { return unquote(t.getUnqualifiedName()); } return unquote(schema) + "." + unquote(t.getUnqualifiedName()); } /** * Removes quotes from table name. * * @param name the table name * @return table name without quotes */ private String unquote(String name) { if (!name.isEmpty()) { char fc = name.charAt(0); if (!Character.isLetterOrDigit(fc) && fc != '_') { String fcStr = Character.toString(fc); if (name.startsWith(fcStr) && name.endsWith(fcStr)) { name = name.substring(1, name.length() - 1); } } } return name; } /** * Reads result-set and writes into export-script. */ public void readCurrentRow(ResultSet resultSet) throws SQLException { if (columnLabel == null) { columnCount = getMetaData(resultSet).getColumnCount(); columnLabel = new String[columnCount + 1]; for (int i = 1; i <= columnCount; ++i) { String mdColumnLabel = getMetaData(resultSet) .getColumnLabel(i); columnLabel[i] = mdColumnLabel; } } try { AttributesImpl attr = new AttributesImpl(); for (int i = 1; i <= columnCount; ++i) { if (columnLabel[i] == null) { continue; } String value = getValue(resultSet, i, typeCache, dbms); if (value != null) { attr.addAttribute("", "", columnLabel[i], "CDATA", value); } } synchronized (transformerHandler) { transformerHandler.startElement("", "", rowElementName, attr); transformerHandler.endElement("", "", rowElementName); } } catch (SAXException e) { throw new RuntimeException(e); } } /** * Gets value from current row a result-set. * * @param resultSet * result-set * @param i * column index * @param typeCache * for caching types * @param dbms * @return object */ private String getValue(ResultSet resultSet, int i, Map<Integer, Integer> typeCache, DBMS dbms) throws SQLException { Object object; Integer type = typeCache.get(i); if (type == null) { try { type = getMetaData(resultSet).getColumnType(i); if (DBMS.ORACLE.equals(dbms)) { if (type == Types.DATE) { type = Types.TIMESTAMP; } } } catch (SQLException e) { type = Types.OTHER; } typeCache.put(i, type); } if (type == Types.TIMESTAMP) { object = resultSet.getTimestamp(i); } else if (type == Types.TIME) { object = resultSet.getTime(i); } else if (type == Types.DATE) { object = resultSet.getDate(i); } else { object = resultSet.getObject(i); } if (object == null || resultSet.wasNull()) { return Configuration.getInstance().getNullColumnPlaceholder(); } if (object instanceof byte[]) { return Base64.encodeBytes((byte[]) object); } if (object instanceof Blob) { Blob blob = (Blob) object; byte[] blobValue = blob.getBytes(1, (int) blob.length()); return Base64.encodeBytes(blobValue); } if (object instanceof Clob) { Clob clobValue = (Clob) object; int length = (int) clobValue.length(); if (length > 0) { return clobValue.getSubString(1, length); } return ""; } return object.toString(); }; /** * Finalizes reading. */ public void close() { if (columnLabel != null) { synchronized (transformerHandler) { try { String content = "\n\n "; transformerHandler.characters(content.toCharArray(), 0, content.length()); } catch (SAXException e) { throw new RuntimeException(e); } } } } }