All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.imsweb.validation.translation.metafile.MetafileUtils Maven / Gradle / Ivy

Go to download

This project allows a Genedits Metafile to be translated into Groovy edits that can be executed in the SEER Validation framework.

The newest version!
/*
 * Copyright (C) 2016 Information Management Services, Inc.
 */
package com.imsweb.validation.translation.metafile;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public final class MetafileUtils {

    // this class needs to use try-with-resources and use proper SQL parameters, but those are not trivial changes; for now I disabled the spotbug warnings

    public static Metafile extractMetafileInformation(File smf) {
        if (!smf.exists() || !smf.isFile())
            throw new RuntimeException("Unable to open '" + smf.getPath() + "'");

        try {
            Class.forName("org.sqlite.JDBC");
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException("Unable to open find drivers for SQLite");
        }

        try (Connection con = DriverManager.getConnection("jdbc:sqlite:" + smf.getPath())) {

            // collect control information
            Map controlInfo = new HashMap<>();
            Statement stmt = con.createStatement();
            ResultSet result = stmt.executeQuery("select * from EE_Control;");
            while (result.next())
                controlInfo.put(result.getString("ItemName"), result.getString("ItemValue"));
            result.close();
            stmt.close();

            // collect agencies
            Map agencies = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_Agencies;");
            while (result.next()) {
                MetafileAgency agency = new MetafileAgency();
                agency.setPrimaryKey(result.getInt("PKey"));
                agency.setAdminCode(result.getString("AdminCode"));
                agency.setOrganizationName(result.getString("OrgName"));
                agency.setLastModified(readDate(result.getString("LastModDate")));
                agency.setComments(result.getString("Comments"));
                agencies.put(agency.getPrimaryKey(), agency);
            }
            result.close();
            stmt.close();

            // collect messages
            Map messages = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_Messages;");
            while (result.next()) {
                MetafileMessage message = new MetafileMessage();
                message.setPrimaryKey(result.getInt("PKey"));
                message.setAgency(agencies.get(result.getInt("AgencyPKey")));
                message.setNumber(result.getInt("MessageNumber"));
                message.setMessage(result.getString("Message"));
                message.setLastModified(readDate(result.getString("LastModDate")));
                messages.put(message.getPrimaryKey(), message);
            }
            result.close();
            stmt.close();

            // collect fields
            Map fields = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_Fields;");
            while (result.next()) {
                MetafileField field = new MetafileField();
                field.setPrimaryKey(result.getInt("PKey"));
                field.setAgency(agencies.get(result.getInt("AgencyPKey")));
                field.setNumber(result.getInt("FieldNumber"));
                field.setName(result.getString("FieldName"));
                field.setLocalName(result.getString("LocalName"));
                field.setLength(result.getInt("FieldLength"));
                field.setType(result.getString("DataType"));
                field.setLastModified(readDate(result.getString("LastModDate")));
                field.setComments(result.getString("Comments"));
                fields.put(field.getPrimaryKey(), field);
            }
            result.close();
            stmt.close();

            // collect sets
            Map sets = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_EditSets;");
            while (result.next()) {
                MetafileSet set = new MetafileSet();
                set.setPrimaryKey(result.getInt("PKey"));
                set.setAgency(agencies.get(result.getInt("AgencyPKey")));
                set.setTag(result.getString("EditSetTag"));
                set.setName(result.getString("EditSetName"));
                set.setLastModified(readDate(result.getString("LastModDate")));
                set.setDescription(result.getString("Description"));
                sets.put(set.getPrimaryKey(), set);
            }
            result.close();
            stmt.close();

            // collect tables
            Map tables = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_LookupTables;");
            while (result.next()) {
                MetafileTable table = new MetafileTable();
                table.setPrimaryKey(result.getInt("PKey"));
                table.setAgency(agencies.get(result.getInt("AgencyPKey")));
                table.setName(result.getString("TableName").toUpperCase());
                table.setRecordCount(result.getInt("RecCount"));
                table.setLastModified(readDate(result.getString("LastModDate")));
                table.setComments(result.getString("Comments"));

                // collect the triggers for this table, this will tell us which columns are "fake" ones (triggered-maintained indexes)
                List triggeredMaintainedIndexNames = new ArrayList<>();
                Pattern p = Pattern.compile("^CREATE TRIGGER update.+UPDATE " + table.getName() + "\\s+SET \\[(.+?)].+$", Pattern.MULTILINE | Pattern.DOTALL);
                Statement stmt2 = con.createStatement();
                ResultSet result2 = stmt2.executeQuery("select sql from sqlite_master where type = 'trigger' and tbl_name = '" + table.getName() + "';");
                while (result2.next()) {
                    String sql = result2.getString("sql");
                    Matcher m = p.matcher(sql);
                    if (m.matches())
                        triggeredMaintainedIndexNames.add(m.group(1));
                }
                result2.close();
                stmt2.close();
                table.setTriggeredMaintainedIndexes(triggeredMaintainedIndexNames);

                // collect table headers and content
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select * from " + table.getName() + ";");
                List headers = new ArrayList<>();
                Map columnsMetaData = new HashMap<>();
                for (int i = 2; i <= result2.getMetaData().getColumnCount(); i++) { // 1-based and ignore first PKey column
                    headers.add(result2.getMetaData().getColumnName(i).toUpperCase());
                    MetafileColumnMetaData data = new MetafileColumnMetaData();
                    data.setColumnName(result2.getMetaData().getColumnName(i).toUpperCase());
                    data.setColumnType(result2.getMetaData().getColumnTypeName(i));
                    // it looks like the columns size (the precision) is not properly returned for triggered-maintain columns (it's always set to 1); for now I deal with it
                    // in the translation, but a better approach would be to parse the trigger, gather the concatenated columns, and add their size...
                    data.setColumnSize(result2.getMetaData().getPrecision(i));
                    columnsMetaData.put(result2.getMetaData().getColumnName(i), data);
                    // make sure we expect the column type
                    if (!"CHAR".equals(result2.getMetaData().getColumnTypeName(i)) && !"VARCHAR".equals(result2.getMetaData().getColumnTypeName(i)) && !"INTEGER".equals(result2.getMetaData().getColumnTypeName(i)))
                        throw new RuntimeException("Unsupported type for table '" + table.getName() + ": " + result2.getMetaData().getColumnTypeName(i));
                }
                table.setHeaders(headers);
                table.setMetaData(columnsMetaData);

                // populate table content
                List> content = new ArrayList<>();
                while (result2.next()) {
                    List row = new ArrayList<>();
                    for (int i = 2; i <= result2.getMetaData().getColumnCount(); i++) // 1-based and ignore first PKey column
                        row.add(result2.getString(i));
                    content.add(row);
                }
                table.setContent(content);
                result2.close();
                stmt2.close();

                // collect the single-column indexes
                Map> singleColumnIndexes = new HashMap<>();
                p = Pattern.compile("^CREATE INDEX.+\\(\\[(.+?)].+$");
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select sql from sqlite_master where type = 'index' and tbl_name = '" + table.getName() + "';");
                while (result2.next()) {
                    Matcher m = p.matcher(result2.getString("sql"));
                    if (m.matches() && !triggeredMaintainedIndexNames.contains(m.group(1))) {
                        boolean allowDup = result2.getString("sql").contains("[PKey]");
                        int columnIdx = headers.indexOf(m.group(1).toUpperCase());
                        List indexContent = new ArrayList<>();
                        for (List row : content) {
                            String val = row.get(columnIdx);
                            if (allowDup || !indexContent.contains(val))
                                indexContent.add(val);
                        }
                        singleColumnIndexes.put(m.group(1), indexContent);
                    }
                }
                table.setSingleColumnIndexes(singleColumnIndexes);
                result2.close();
                stmt2.close();

                tables.put(table.getPrimaryKey(), table);
            }
            result.close();
            stmt.close();

            // collect edits
            Map edits = new HashMap<>();
            stmt = con.createStatement();
            result = stmt.executeQuery("select * from EE_Edits;");
            while (result.next()) {
                MetafileEdit edit = new MetafileEdit();
                edit.setPrimaryKey(result.getInt("PKey"));
                edit.setAgency(agencies.get(result.getInt("AgencyPKey")));
                edit.setTag(result.getString("EditTag"));
                edit.setName(result.getString("EditName"));
                edit.setDefaultMessage(messages.get(result.getInt("MessagePKey")));

                // collect messages used by this edit
                Statement stmt2 = con.createStatement();
                ResultSet result2 = stmt2.executeQuery("select * from EE_EditMessagesPivot where EditPKey = " + edit.getPrimaryKey()+ ";");
                List messagesForEdit = new ArrayList<>();
                while (result2.next())
                    messagesForEdit.add(messages.get(result2.getInt("MessagePKey")));
                edit.setMessages(messagesForEdit);
                result2.close();
                stmt2.close();

                // collect free error messages used by this edit
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select * from EE_EditErrorsPivot where EditPKey = " + edit.getPrimaryKey() + ";");
                List errorsForEdit = new ArrayList<>();
                while (result2.next())
                    errorsForEdit.add(result2.getString("ErrorText"));
                edit.setErrorMessages(errorsForEdit);
                result2.close();
                stmt2.close();

                // collect the fields used by this edit
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select * from EE_EditFieldsPivot where EditPKey = " + edit.getPrimaryKey() + ";");
                Map fieldsForEdit = new TreeMap<>();
                while (result2.next())
                    fieldsForEdit.put(result2.getInt("FieldOrder"), fields.get(result2.getInt("FieldPKey")));
                edit.setFields(fieldsForEdit);
                result2.close();
                stmt2.close();

                // collect the tables used by this edit
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select * from EE_EditTablesPivot where EditPKey = " + edit.getPrimaryKey() + ";");
                List tablesForEdit = new ArrayList<>();
                while (result2.next())
                    tablesForEdit.add(tables.get(result2.getInt("LookupTablePKey")));
                edit.setTables(tablesForEdit);
                result2.close();
                stmt2.close();

                // collect the sets using this edit
                stmt2 = con.createStatement();
                result2 = stmt2.executeQuery("select * from EE_EditSetEditsPivot where EditPKey = " + edit.getPrimaryKey() + ";");
                List setsForEdit = new ArrayList<>();
                while (result2.next())
                    setsForEdit.add(sets.get(result2.getInt("EditSetPKey")));
                edit.setSets(setsForEdit);
                result2.close();
                stmt2.close();

                edit.setLastModified(readDate(result.getString("LastModDate")));
                edit.setDescription(result.getString("Description"));
                edit.setLogic(result.getString("EditLogic").replaceAll("\\t", "    ").replaceAll("\r\n", "\n").replaceAll("(^\\s+|^\\n+|\\s+$|\\n+$)", ""));
                edit.setAdminNotes(result.getString("AdminNotes"));
                edits.put(edit.getPrimaryKey(), edit);
            }
            result.close();
            stmt.close();

            Metafile metafile = new Metafile();
            metafile.setName(smf.getName());
            metafile.setStructureVersion(controlInfo.get("MFStructureVersion"));
            metafile.setContentVersion(controlInfo.get("MFContentVersion"));
            metafile.setComment(controlInfo.get("MFComment"));
            metafile.setAgencies(new ArrayList<>(agencies.values()));
            metafile.setMessages(new ArrayList<>(messages.values()));
            metafile.setFields(new ArrayList<>(fields.values()));
            metafile.setEdits(new ArrayList<>(edits.values()));
            metafile.setSets(new ArrayList<>(sets.values()));
            metafile.setTables(new ArrayList<>(tables.values()));
            return metafile;

        }
        catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static LocalDateTime readDate(String value) {
        if (value == null || value.isEmpty())
            return null;

        if (value.matches("\\d{8}"))
            return LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyyMMdd")).atStartOfDay();

        if (value.matches("\\d{8} \\d{2}:\\d{2}\\d{2}"))
            return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyyMMdd hh:mm:ss"));

        return null;
    }

    // this replaces the F1, F2, etc... and V1, V2, etc... by the field names and/or values
    public static String fixMessage(String msg, MetafileEdit edit, boolean fixDc) {
        List fieldIndexes = edit.getFields().keySet().stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

        for (int fieldIdx : fieldIndexes) {
            MetafileField field = edit.getFields().get(fieldIdx);
            if (field.getPropertyName() == null)
                throw new RuntimeException("Trying to reference a missing property name for field " + field.getName());
            String valReference = "${untrimmedline." + field.getPropertyName() + ("D1".equalsIgnoreCase(field.getType()) ? ".formatDate()" : "") + "}";
            msg = msg.replace("%F" + fieldIdx, field.getName()).replace("%V" + fieldIdx, valReference);
        }

        // %DC is dynamically replaced by an type of invalid message (future date, invalid year, etc...) when the message is set in the logic
        if (fixDc)
            msg = msg.replace("%DC", "invalid");

        return msg;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy