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