// Path: src/main/java/info/gianlucacosta/easypmd/pmdscanner/ScanMessage.java
// public interface ScanMessage extends Serializable {
// 
//     boolean isShowableInGuardedSections();
// 
//     int getLineNumber();
// 
//     Task createTask(Options options, FileObject fileObject);
// 
//     Annotation createAnnotation(Options options);
// }
// 
// Path: src/main/java/info/gianlucacosta/easypmd/util/Throwables.java
// public interface Throwables {
// 
//     /**
//      * Converts the stack trace of a Throwable to a string
//      *
//      * @param throwable
//      * @return the string representation of the stack trace
//      */
//     static String getStackTraceString(Throwable throwable) {
//         final Writer result = new StringWriter();
//         final PrintWriter printWriter = new PrintWriter(result);
// 
//         throwable.printStackTrace(printWriter);
// 
//         return result.toString();
//     }
// 
//     /**
//      * Returns the throwable message, or the message of its cause, recursively,
//      * or a simple message if no message was found in the chain
//      *
//      * @param throwable The subject throwable
//      * @return a non-empty string
//      */
//     static String getNonEmptyMessage(Throwable throwable) {
//         String message = throwable.getMessage();
// 
//         if (message == null || message.isEmpty()) {
//             Throwable cause = throwable.getCause();
// 
//             if (cause != null) {
//                 return getNonEmptyMessage(cause);
//             } else {
//                 return "(no message)";
//             }
//         } else {
//             return message;
//         }
//     }
// 
//     /**
//      * Returns a string showing a Throwable (type and message) and the related
//      * stack trace
//      *
//      * @param throwable
//      * @return
//      */
//     static String toStringWithStackTrace(Throwable throwable) {
//         return String.format(
//                 "%s (%s)\n%s",
//                 throwable.getClass().getName(),
//                 throwable.getMessage(),
//                 getStackTraceString(throwable)
//         );
//     }
// }


import java.sql.SQLException;
import java.sql.Statement;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import info.gianlucacosta.easypmd.pmdscanner.ScanMessage;
import info.gianlucacosta.easypmd.util.Throwables;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
            updateStatement = dbConnection.prepareStatement(UPDATE_DDL);

            clearStatement = dbConnection.prepareStatement(CLEAR_DDL);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public Optional<CacheEntry> getEntry(String pathString) {
        try {
            selectStatement.setString(1, pathString);

            try (ResultSet resultSet = selectStatement.executeQuery()) {
                if (resultSet.next()) {
                    long lastModificationMillis = resultSet.getLong(1);
                    Set<ScanMessage> scanMessages = (Set<ScanMessage>) resultSet.getObject(2);

                    CacheEntry cacheEntry = new CacheEntry(lastModificationMillis, scanMessages);

                    return Optional.of(cacheEntry);
                } else {
                    return Optional.empty();
                }
            }
        } catch (Exception ex) {
            logger.warning(
                    () -> String.format(
                            "Error while retrieving db-cached scan messages for path: %s.\nCause: %s",
                            pathString,
// Path: src/main/java/info/gianlucacosta/easypmd/pmdscanner/ScanMessage.java
// public interface ScanMessage extends Serializable {
// 
//     boolean isShowableInGuardedSections();
// 
//     int getLineNumber();
// 
//     Task createTask(Options options, FileObject fileObject);
// 
//     Annotation createAnnotation(Options options);
// }
// 
// Path: src/main/java/info/gianlucacosta/easypmd/util/Throwables.java
// public interface Throwables {
// 
//     /**
//      * Converts the stack trace of a Throwable to a string
//      *
//      * @param throwable
//      * @return the string representation of the stack trace
//      */
//     static String getStackTraceString(Throwable throwable) {
//         final Writer result = new StringWriter();
//         final PrintWriter printWriter = new PrintWriter(result);
// 
//         throwable.printStackTrace(printWriter);
// 
//         return result.toString();
//     }
// 
//     /**
//      * Returns the throwable message, or the message of its cause, recursively,
//      * or a simple message if no message was found in the chain
//      *
//      * @param throwable The subject throwable
//      * @return a non-empty string
//      */
//     static String getNonEmptyMessage(Throwable throwable) {
//         String message = throwable.getMessage();
// 
//         if (message == null || message.isEmpty()) {
//             Throwable cause = throwable.getCause();
// 
//             if (cause != null) {
//                 return getNonEmptyMessage(cause);
//             } else {
//                 return "(no message)";
//             }
//         } else {
//             return message;
//         }
//     }
// 
//     /**
//      * Returns a string showing a Throwable (type and message) and the related
//      * stack trace
//      *
//      * @param throwable
//      * @return
//      */
//     static String toStringWithStackTrace(Throwable throwable) {
//         return String.format(
//                 "%s (%s)\n%s",
//                 throwable.getClass().getName(),
//                 throwable.getMessage(),
//                 getStackTraceString(throwable)
//         );
//     }
// }


// Path: src/main/java/info/gianlucacosta/easypmd/pmdscanner/messages/cache/HsqlDbStorage.java
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import info.gianlucacosta.easypmd.pmdscanner.ScanMessage;
import info.gianlucacosta.easypmd.util.Throwables;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

            updateStatement = dbConnection.prepareStatement(UPDATE_DDL);

            clearStatement = dbConnection.prepareStatement(CLEAR_DDL);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public Optional<CacheEntry> getEntry(String pathString) {
        try {
            selectStatement.setString(1, pathString);

            try (ResultSet resultSet = selectStatement.executeQuery()) {
                if (resultSet.next()) {
                    long lastModificationMillis = resultSet.getLong(1);
                    Set<ScanMessage> scanMessages = (Set<ScanMessage>) resultSet.getObject(2);

                    CacheEntry cacheEntry = new CacheEntry(lastModificationMillis, scanMessages);

                    return Optional.of(cacheEntry);
                } else {
                    return Optional.empty();
                }
            }
        } catch (Exception ex) {
            logger.warning(
                    () -> String.format(
                            "Error while retrieving db-cached scan messages for path: %s.\nCause: %s",
                            pathString,
                            Throwables.toStringWithStackTrace(ex)