001/*
002 * Copyright 2015-2022 Transmogrify LLC, 2022-2025 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.pyranid;
018
019import javax.annotation.Nonnull;
020import javax.annotation.Nullable;
021import javax.annotation.concurrent.NotThreadSafe;
022import javax.annotation.concurrent.ThreadSafe;
023import java.time.ZoneId;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.List;
028import java.util.Objects;
029import java.util.Optional;
030import java.util.stream.Collectors;
031
032import static java.lang.String.format;
033import static java.util.Objects.requireNonNull;
034
035/**
036 * Data that represents a SQL statement.
037 *
038 * @author <a href="https://www.revetkn.com">Mark Allen</a>
039 * @since 2.0.0
040 */
041@ThreadSafe
042public final class StatementContext<T> {
043        @Nonnull
044        private final Statement statement;
045        @Nonnull
046        private final List<Object> parameters;
047        @Nullable
048        private final Class<T> resultSetRowType;
049        @Nonnull
050        private final DatabaseType databaseType;
051        @Nonnull
052        private final ZoneId timeZone;
053
054        protected StatementContext(@Nonnull Builder builder) {
055                requireNonNull(builder);
056
057                this.statement = builder.statement;
058                this.parameters = builder.parameters == null ? List.of() : Collections.unmodifiableList(builder.parameters);
059                this.resultSetRowType = builder.resultSetRowType;
060                this.databaseType = builder.databaseType;
061                this.timeZone = builder.timeZone;
062        }
063
064        @Override
065        public int hashCode() {
066                return Objects.hash(getStatement(), getParameters(), getResultSetRowType(), getDatabaseType(), getTimeZone());
067        }
068
069        @Override
070        public boolean equals(Object object) {
071                if (this == object)
072                        return true;
073
074                if (!(object instanceof StatementContext))
075                        return false;
076
077                StatementContext statementContext = (StatementContext) object;
078
079                return Objects.equals(statementContext.getStatement(), getStatement())
080                                && Objects.equals(statementContext.getParameters(), getParameters())
081                                && Objects.equals(statementContext.getResultSetRowType(), getResultSetRowType())
082                                && Objects.equals(statementContext.getDatabaseType(), getDatabaseType())
083                                && Objects.equals(statementContext.getTimeZone(), getTimeZone());
084        }
085
086        @Override
087        public String toString() {
088                List<String> components = new ArrayList<>(3);
089
090                components.add(format("statement=%s", getStatement()));
091
092                if (getParameters().size() > 0)
093                        components.add(format("parameters=%s", getParameters()));
094
095                Class<T> resultSetRowType = getResultSetRowType().orElse(null);
096
097                if (resultSetRowType != null)
098                        components.add(format("resultSetRowType=%s", resultSetRowType));
099
100                components.add(format("databaseType=%s", getDatabaseType().name()));
101                components.add(format("timeZone=%s", getTimeZone().getId()));
102
103                return format("%s{%s}", getClass().getSimpleName(), components.stream().collect(Collectors.joining(", ")));
104        }
105
106        @Nonnull
107        public Statement getStatement() {
108                return this.statement;
109        }
110
111        @Nonnull
112        public List<Object> getParameters() {
113                return this.parameters;
114        }
115
116        @Nonnull
117        public Optional<Class<T>> getResultSetRowType() {
118                return Optional.ofNullable(this.resultSetRowType);
119        }
120
121        @Nonnull
122        public DatabaseType getDatabaseType() {
123                return this.databaseType;
124        }
125
126        @Nonnull
127        public ZoneId getTimeZone() {
128                return this.timeZone;
129        }
130
131        @Nonnull
132        public static <T> Builder<T> with(@Nonnull Statement statement,
133                                                                                                                                                @Nonnull Database database) {
134                requireNonNull(statement);
135                requireNonNull(database);
136
137                return new Builder<>(statement, database);
138        }
139
140        /**
141         * Builder used to construct instances of {@link StatementContext}.
142         * <p>
143         * This class is intended for use by a single thread.
144         *
145         * @author <a href="https://www.revetkn.com">Mark Allen</a>
146         * @since 2.0.0
147         */
148        @NotThreadSafe
149        public static class Builder<T> {
150                @Nonnull
151                private final Statement statement;
152                @Nonnull
153                private final DatabaseType databaseType;
154                @Nonnull
155                private final ZoneId timeZone;
156                @Nullable
157                private List<Object> parameters;
158                @Nullable
159                private Class<T> resultSetRowType;
160
161                private Builder(@Nonnull Statement statement,
162                                                                                @Nonnull Database database) {
163                        requireNonNull(statement);
164                        requireNonNull(database);
165
166                        this.statement = statement;
167                        this.databaseType = database.getDatabaseType();
168                        this.timeZone = database.getTimeZone();
169                }
170
171                @Nonnull
172                public Builder parameters(@Nullable List<Object> parameters) {
173                        this.parameters = parameters;
174                        return this;
175                }
176
177                @Nonnull
178                public Builder parameters(@Nullable Object... parameters) {
179                        this.parameters = parameters == null ? null : Arrays.asList(parameters);
180                        return this;
181                }
182
183                @Nonnull
184                public Builder resultSetRowType(Class<T> resultSetRowType) {
185                        this.resultSetRowType = resultSetRowType;
186                        return this;
187                }
188
189                @Nonnull
190                public StatementContext build() {
191                        return new StatementContext(this);
192                }
193        }
194}