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