001/**
002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com
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 */
016package org.talend.sdk.component.api.record;
017
018import static java.util.Optional.ofNullable;
019
020import java.math.BigDecimal;
021import java.time.Instant;
022import java.time.ZonedDateTime;
023import java.util.Collection;
024import java.util.Comparator;
025import java.util.Date;
026import java.util.List;
027import java.util.Optional;
028import java.util.OptionalDouble;
029import java.util.OptionalInt;
030import java.util.OptionalLong;
031import java.util.function.Function;
032
033import org.talend.sdk.component.api.record.Schema.Entry;
034
035public interface Record {
036
037    String RECORD_ERROR_SUPPORT = "talend.component.record.error.support";
038
039    String RECORD_NULLABLE_CHECK = "talend.component.record.nullable.check";
040
041    /**
042     * @return the schema of this record.
043     */
044    Schema getSchema();
045
046    /**
047     * Create a Builder with values of the record present in {@link Schema}.
048     * 
049     * @param schema new schema
050     * @return a {@link Record.Builder}
051     */
052    default Builder withNewSchema(Schema schema) {
053        throw new UnsupportedOperationException("#withNewSchema is not implemented");
054    }
055
056    /**
057     * Access a record field value.
058     *
059     * IMPORTANT: it is always better to use the typed accessors and the optional flavor when the entry is nullable.
060     *
061     * @param expectedType the expected type for the column.
062     * @param name the name of the column.
063     * @param <T> the type of expectedType.
064     * @return the column value.
065     */
066    <T> T get(Class<T> expectedType, String name);
067
068    default <T> T get(Class<T> expectedType, Schema.Entry entry) {
069        if (entry == null) {
070            return null;
071        }
072        return this.get(expectedType, entry.getName());
073    }
074
075    /**
076     * See {@link Record#get(Class, String)}.
077     * 
078     * @param name entry name.
079     * @return the value of the entry in this record.
080     */
081    default String getString(final String name) {
082        return get(String.class, name);
083    }
084
085    /**
086     * See {@link Record#get(Class, String)}.
087     * 
088     * @param name entry name.
089     * @return the value of the entry in this record.
090     */
091    default int getInt(final String name) {
092        return get(Integer.class, name);
093    }
094
095    /**
096     * See {@link Record#get(Class, String)}.
097     * 
098     * @param name entry name.
099     * @return the value of the entry in this record.
100     */
101    default long getLong(final String name) {
102        return get(Long.class, name);
103    }
104
105    /**
106     * See {@link Record#get(Class, String)}.
107     * 
108     * @param name entry name.
109     * @return the value of the entry in this record.
110     */
111    default double getDouble(final String name) {
112        return get(Double.class, name);
113    }
114
115    /**
116     * See {@link Record#get(Class, String)}.
117     * 
118     * @param name entry name.
119     * @return the value of the entry in this record.
120     */
121    default float getFloat(final String name) {
122        return get(Float.class, name);
123    }
124
125    /**
126     * See {@link Record#get(Class, String)}.
127     * 
128     * @param name entry name.
129     * @return the value of the entry in this record.
130     */
131    default boolean getBoolean(final String name) {
132        return get(Boolean.class, name);
133    }
134
135    /**
136     * See {@link Record#get(Class, String)}.
137     * 
138     * @param name entry name.
139     * @return the value of the entry in this record.
140     */
141    default byte[] getBytes(final String name) {
142        return get(byte[].class, name);
143    }
144
145    /**
146     * See {@link Record#get(Class, String)}.
147     * 
148     * @param name entry name.
149     * @return the value of the entry in this record.
150     */
151    default Record getRecord(final String name) {
152        return get(Record.class, name);
153    }
154
155    /**
156     * See {@link Record#get(Class, String)}.
157     * 
158     * @param type type of the elements of the collection.
159     * @param name entry name.
160     * @param <T> type of the collection elements.
161     * @return the value of the entry in this record.
162     */
163    default <T> Collection<T> getArray(final Class<T> type, final String name) {
164        return get(Collection.class, name);
165    }
166
167    /**
168     * See {@link Record#get(Class, String)}.
169     * 
170     * @param name entry name.
171     * @return the value of the entry in this record.
172     */
173    default ZonedDateTime getDateTime(final String name) {
174        return get(ZonedDateTime.class, name);
175    }
176
177    /**
178     * See {@link Record#get(Class, String)}.
179     *
180     * @param name entry name.
181     * @return the value of the entry in this record.
182     */
183    default Instant getInstant(final String name) {
184        return get(Instant.class, name);
185    }
186
187    default BigDecimal getDecimal(final String name) {
188        return get(BigDecimal.class, name);
189    }
190
191    /**
192     * See {@link Record#get(Class, String)}.
193     * 
194     * @param type type of the elements of the collection.
195     * @param name entry name.
196     * @param <T> type of the collection elements.
197     * @return the value of the entry in this record.
198     */
199    default <T> Optional<Collection<T>> getOptionalArray(final Class<T> type, final String name) {
200        final Collection<T> value = get(Collection.class, name);
201        return ofNullable(value);
202    }
203
204    /**
205     * See {@link Record#get(Class, String)}.
206     * 
207     * @param name entry name.
208     * @return the value of the entry in this record.
209     */
210    default Optional<ZonedDateTime> getOptionalDateTime(final String name) {
211        return ofNullable(get(ZonedDateTime.class, name));
212    }
213
214    /**
215     * See {@link Record#get(Class, String)}.
216     *
217     * @param name entry name.
218     * @return the value of the entry in this record.
219     */
220    default Optional<Instant> getOptionalInstant(final String name) {
221        return ofNullable(get(Instant.class, name));
222    }
223
224    /**
225     * See {@link Record#get(Class, String)}.
226     *
227     * @param name entry name.
228     * @return the value of the entry in this record.
229     */
230    default Optional<BigDecimal> getOptionalDecimal(final String name) {
231        return ofNullable(get(BigDecimal.class, name));
232    }
233
234    /**
235     * See {@link Record#get(Class, String)}.
236     * 
237     * @param name entry name.
238     * @return the value of the entry in this record.
239     */
240    default Optional<String> getOptionalString(final String name) {
241        return ofNullable(get(String.class, name));
242    }
243
244    /**
245     * See {@link Record#get(Class, String)}.
246     * 
247     * @param name entry name.
248     * @return the value of the entry in this record.
249     */
250    default OptionalInt getOptionalInt(final String name) {
251        final Integer value = get(Integer.class, name);
252        return value == null ? OptionalInt.empty() : OptionalInt.of(value);
253    }
254
255    /**
256     * See {@link Record#get(Class, String)}.
257     * 
258     * @param name entry name.
259     * @return the value of the entry in this record.
260     */
261    default OptionalLong getOptionalLong(final String name) {
262        final Long value = get(Long.class, name);
263        return value == null ? OptionalLong.empty() : OptionalLong.of(value);
264    }
265
266    /**
267     * See {@link Record#get(Class, String)}.
268     * 
269     * @param name entry name.
270     * @return the value of the entry in this record.
271     */
272    default OptionalDouble getOptionalDouble(final String name) {
273        final Double value = get(Double.class, name);
274        return value == null ? OptionalDouble.empty() : OptionalDouble.of(value);
275    }
276
277    /**
278     * See {@link Record#get(Class, String)}.
279     * 
280     * @param name entry name.
281     * @return the value of the entry in this record.
282     */
283    default OptionalDouble getOptionalFloat(final String name) {
284        final Float value = get(Float.class, name);
285        return value == null ? OptionalDouble.empty() : OptionalDouble.of(value);
286    }
287
288    /**
289     * See {@link Record#get(Class, String)}.
290     * 
291     * @param name entry name.
292     * @return the value of the entry in this record.
293     */
294    default Optional<Boolean> getOptionalBoolean(final String name) {
295        return ofNullable(get(Boolean.class, name));
296    }
297
298    /**
299     * See {@link Record#get(Class, String)}.
300     * 
301     * @param name entry name.
302     * @return the value of the entry in this record.
303     */
304    default Optional<byte[]> getOptionalBytes(final String name) {
305        return ofNullable(get(byte[].class, name));
306    }
307
308    /**
309     * See {@link Record#get(Class, String)}.
310     * 
311     * @param name entry name.
312     * @return the value of the entry in this record.
313     */
314    default Optional<Record> getOptionalRecord(final String name) {
315        return ofNullable(get(Record.class, name));
316    }
317
318    default boolean isValid() {
319        return !getSchema().getAllEntries()
320                .anyMatch(entry -> !entry.isValid());
321    }
322
323    /**
324     * Allows to create a record with a fluent API. This is the unique recommended way to create a record.
325     */
326
327    interface Builder {
328
329        Record build();
330
331        Object getValue(String name);
332
333        List<Entry> getCurrentEntries();
334
335        default Entry getEntry(final String name) {
336            return this.getCurrentEntries()
337                    .stream()
338                    .filter((Entry e) -> name.equals(e.getName()))
339                    .findFirst()
340                    .orElse(null);
341        }
342
343        /**
344         * Mark that next entry created {@code withXXXX()} will be before {@code entryName} in schema order.
345         *
346         * @see
347         * <ul>
348         * <li>{@link Schema#naturalOrder()}</li>
349         * <li>{@link Schema#getEntriesOrdered()}</li>
350         * <li>{@link Schema#getEntriesOrdered(Comparator)}</li>
351         * </ul>
352         *
353         * @param entryName target entry name. This entry <b>must</b> exist!
354         *
355         * @return this Builder
356         */
357        default Builder before(String entryName) {
358            throw new UnsupportedOperationException("#before is not implemented");
359        }
360
361        /**
362         * Mark that next entry created {@code withXXXX()} will be after {@code entryName} in schema order.
363         *
364         * @see
365         * <ul>
366         * <li>{@link Schema#naturalOrder()}</li>
367         * <li>{@link Schema#getEntriesOrdered()}</li>
368         * <li>{@link Schema#getEntriesOrdered(Comparator)}</li>
369         * </ul>
370         *
371         * @param entryName target entry name. This entry <b>must</b> exist!
372         *
373         * @return this Builder
374         */
375        default Builder after(String entryName) {
376            throw new UnsupportedOperationException("#after");
377        }
378
379        Builder removeEntry(Schema.Entry schemaEntry);
380
381        Builder updateEntryByName(String name, Schema.Entry schemaEntry);
382
383        default Builder updateEntryByName(String name,
384                Schema.Entry schemaEntry,
385                Function<Object, Object> valueCastFunction) {
386            throw new UnsupportedOperationException("#updateEntryByName");
387        }
388
389        Builder with(Schema.Entry entry, Object value);
390
391        Builder withString(String name, String value);
392
393        Builder withString(Schema.Entry entry, String value);
394
395        Builder withBytes(String name, byte[] value);
396
397        Builder withBytes(Schema.Entry entry, byte[] value);
398
399        Builder withDateTime(String name, Date value);
400
401        Builder withDateTime(Schema.Entry entry, Date value);
402
403        Builder withDateTime(String name, ZonedDateTime value);
404
405        Builder withDateTime(Schema.Entry entry, ZonedDateTime value);
406
407        default Builder withDecimal(String name, BigDecimal value) {
408            throw new UnsupportedOperationException("#withDecimal");
409        }
410
411        default Builder withDecimal(Schema.Entry entry, BigDecimal value) {
412            throw new UnsupportedOperationException("#withDecimal");
413        }
414
415        Builder withTimestamp(String name, long value);
416
417        Builder withTimestamp(Schema.Entry entry, long value);
418
419        default Builder withInstant(String name, Instant value) {
420            throw new UnsupportedOperationException("#withInstant");
421        }
422
423        default Builder withInstant(Schema.Entry entry, Instant value) {
424            throw new UnsupportedOperationException("#withInstant");
425        }
426
427        Builder withInt(String name, int value);
428
429        Builder withInt(Schema.Entry entry, int value);
430
431        Builder withLong(String name, long value);
432
433        Builder withLong(Schema.Entry entry, long value);
434
435        Builder withFloat(String name, float value);
436
437        Builder withFloat(Schema.Entry entry, float value);
438
439        Builder withDouble(String name, double value);
440
441        Builder withDouble(Schema.Entry entry, double value);
442
443        Builder withBoolean(String name, boolean value);
444
445        Builder withBoolean(Schema.Entry entry, boolean value);
446
447        Builder withRecord(Schema.Entry entry, Record value);
448
449        /**
450         * @since 1.1.6
451         *
452         * @param name entry name.
453         * @param value record value.
454         * @return this builder.
455         */
456        Builder withRecord(String name, Record value);
457
458        <T> Builder withArray(Schema.Entry entry, Collection<T> values);
459    }
460}