EnumValueMapper.java

/*
 * Copyright 2018-2019 Providence Authors
 *
 * 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.
 */
package net.morimekta.providence.jdbi.v2;

import net.morimekta.providence.PEnumValue;
import net.morimekta.providence.descriptor.PEnumDescriptor;
import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.exceptions.ResultSetException;
import org.skife.jdbi.v2.tweak.ResultColumnMapper;

import javax.annotation.Nonnull;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

/**
 * Map a result set to a message based on meta information and the message
 * descriptor.
 *
 * @param <E> The enum value type.
 */
public class EnumValueMapper<E extends PEnumValue<E>> implements ResultColumnMapper<E> {
    /**
     * Create a enum value column mapper.
     *
     * @param acceptUnknown If unknown values should be accepted.
     * @param descriptor Message descriptor.
     */
    public EnumValueMapper(boolean acceptUnknown,
                           @Nonnull PEnumDescriptor<E> descriptor) {
        this.acceptUnknown = acceptUnknown;
        this.descriptor = descriptor;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{type=" +
               descriptor.getQualifiedName() + "}";
    }

    private final boolean            acceptUnknown;
    private final PEnumDescriptor<E> descriptor;

    public Type getType() {
        return descriptor.getValues()[0].getClass();
    }

    @Override
    public E mapColumn(ResultSet r, int columnNumber, StatementContext ctx) throws SQLException {
        int columnType = r.getMetaData().getColumnType(columnNumber);
        switch (columnType) {
            case Types.TINYINT:
            case Types.SMALLINT:
            case Types.INTEGER:
            case Types.BIGINT:
            case Types.NUMERIC: {
                int id = r.getInt(columnNumber);
                if (r.wasNull()) {
                    return null;
                }
                E out = descriptor.findById(id);
                if (out != null || acceptUnknown) {
                    return out;
                }
                throw new ResultSetException("Unknown value " + id +
                                             " for enum " +
                                             descriptor.getQualifiedName(),
                                             null, ctx);
            }
            case Types.CLOB:
            case Types.NCHAR:
            case Types.VARCHAR:
            case Types.NVARCHAR:
            case Types.LONGVARCHAR:
            case Types.LONGNVARCHAR: {
                String name = r.getString(columnNumber);
                if (name == null) {
                    return null;
                }
                E out = descriptor.findByName(name);
                if (out != null || acceptUnknown) {
                    return out;
                }
                throw new ResultSetException("Unknown value " + name +
                                             " for enum " +
                                             descriptor.getQualifiedName(),
                                             null, ctx);
            }
            default:
                throw new ResultSetException("Unhandled column type " + r.getMetaData().getColumnTypeName(columnNumber) +
                                             "(" + columnType + ") for enum " +
                                             descriptor.getQualifiedName(),
                                             null, ctx);
        }
    }

    @Override
    public E mapColumn(ResultSet r, String columnLabel, StatementContext ctx) throws SQLException {
        return mapColumn(r, r.findColumn(columnLabel), ctx);
    }
}