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