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