/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Other licenses: * ----------------------------------------------------------------------------- * Commercial licenses for this work are available. These replace the above * ASL 2.0 and offer limited warranties, support, maintenance, and commercial * database integrations. * * For more information, please visit: http://www.jooq.org/licenses * * * * * * * * * * * * * */ package org.jooq.impl; import static java.util.Arrays.asList; import static org.jooq.conf.SettingsTools.updatablePrimaryKeys; import static org.jooq.impl.Tools.indexOrFail; import static org.jooq.impl.Tools.resetChangedOnNotNull; import static org.jooq.impl.Tools.settings; import static org.jooq.impl.Tools.ThreadGuard.Guard.RECORD_TOSTRING; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; import org.jooq.Attachable; import org.jooq.Converter; import org.jooq.DataType; import org.jooq.Field; import org.jooq.JSONFormat; import org.jooq.Name; import org.jooq.Record; import org.jooq.Record1; import org.jooq.Record10; import org.jooq.Record11; import org.jooq.Record12; import org.jooq.Record13; import org.jooq.Record14; import org.jooq.Record15; import org.jooq.Record16; import org.jooq.Record17; import org.jooq.Record18; import org.jooq.Record19; import org.jooq.Record2; import org.jooq.Record20; import org.jooq.Record21; import org.jooq.Record22; import org.jooq.Record3; import org.jooq.Record4; import org.jooq.Record5; import org.jooq.Record6; import org.jooq.Record7; import org.jooq.Record8; import org.jooq.Record9; import org.jooq.RecordMapper; import org.jooq.Result; import org.jooq.Table; import org.jooq.UniqueKey; import org.jooq.XMLFormat; import org.jooq.exception.IOException; import org.jooq.exception.InvalidResultException; import org.jooq.exception.MappingException; import org.jooq.impl.Tools.ThreadGuard; import org.jooq.impl.Tools.ThreadGuard.GuardedOperation; import org.jooq.tools.Convert; import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; /** * A general base class for all {@link Record} types * * @author Lukas Eder */ @SuppressWarnings({ "rawtypes", "unchecked" }) abstract class AbstractRecord extends AbstractStore implements Record { /** * Generated UID */ private static final long serialVersionUID = -6052512608911220404L; private static final JooqLogger log = JooqLogger.getLogger(AbstractRecord.class); final RowImpl fields; final Object[] values; final Object[] originals; final BitSet changed; boolean fetched; AbstractRecord(Collection<? extends Field<?>> fields) { this(new RowImpl(fields)); } AbstractRecord(Field<?>... fields) { this(new RowImpl(fields)); } AbstractRecord(RowImpl fields) { int size = fields.size(); this.fields = fields; this.values = new Object[size]; this.originals = new Object[size]; this.changed = new BitSet(size); } // ------------------------------------------------------------------------ // XXX: Attachable API // ------------------------------------------------------------------------ @Override final List<Attachable> getAttachables() { List<Attachable> result = null; int size = size(); for (int i = 0; i < size; i++) { if (values[i] instanceof Attachable) { if (result == null) result = new ArrayList<Attachable>(); result.add((Attachable) values[i]); } } return result == null ? Collections.<Attachable>emptyList() : result; } // ------------------------------------------------------------------------ // XXX: FieldProvider API // ------------------------------------------------------------------------ @Override public final <T> Field<T> field(Field<T> field) { return fieldsRow().field(field); } @Override public final Field<?> field(String name) { return fieldsRow().field(name); } @Override public final Field<?> field(Name name) { return fieldsRow().field(name); } @Override public final Field<?> field(int index) { return index >= 0 && index < fields.size() ? fields.field(index) : null; } @Override public final Field<?>[] fields() { return fields.fields(); } @Override public final Field<?>[] fields(Field<?>... f) { return fields.fields(f); } @Override public final Field<?>[] fields(String... fieldNames) { return fields.fields(fieldNames); } @Override public final Field<?>[] fields(Name... fieldNames) { return fields.fields(fieldNames); } @Override public final Field<?>[] fields(int... fieldIndexes) { return fields.fields(fieldIndexes); } // ------------------------------------------------------------------------ // XXX: Record API // ------------------------------------------------------------------------ @Override public final int size() { return fields.size(); } @Override public final <T> T get(Field<T> field) { return (T) get(indexOrFail(fieldsRow(), field)); } @Override public final <T> T get(Field<?> field, Class<? extends T> type) { return Convert.convert(get(field), type); } @Override public final <T, U> U get(Field<T> field, Converter<? super T, ? extends U> converter) { return converter.from(get(field)); } @Override public final Object get(int index) { return values[safeIndex(index)]; } @Override public final <T> T get(int index, Class<? extends T> type) { return Convert.convert(get(index), type); } @Override public final <U> U get(int index, Converter<?, ? extends U> converter) { return Convert.convert(get(index), converter); } @Override public final Object get(String fieldName) { return get(indexOrFail(fieldsRow(), fieldName)); } @Override public final <T> T get(String fieldName, Class<? extends T> type) { return Convert.convert(get(fieldName), type); } @Override public final <U> U get(String fieldName, Converter<?, ? extends U> converter) { return Convert.convert(get(fieldName), converter); } @Override public final Object get(Name fieldName) { return get(indexOrFail(fieldsRow(), fieldName)); } @Override public final <T> T get(Name fieldName, Class<? extends T> type) { return Convert.convert(get(fieldName), type); } @Override public final <U> U get(Name fieldName, Converter<?, ? extends U> converter) { return Convert.convert(get(fieldName), converter); } /** * Subclasses may type-unsafely set a value to a record index. This method * takes care of converting the value to the appropriate type. * * @deprecated - Use {@link AbstractRecord#set(int, Object)} instead */ @Deprecated protected final void setValue(int index, Object value) { set(index, value); } protected final void set(int index, Object value) { set(index, (Field) field(index), value); } @Override public final <T> void set(Field<T> field, T value) { set(indexOrFail(fields, field), field, value); } final <T> void set(int index, Field<T> field, T value) { // Relevant issues documenting this method's behaviour: // [#945] Avoid bugs resulting from setting the same value twice // [#948] To allow for controlling the number of hard-parses // To allow for explicitly overriding default values // [#979] Avoid modifying chnaged flag on unchanged primary key values UniqueKey<?> key = getPrimaryKey(); // Normal fields' changed flag is always set to true if (key == null || !key.getFields().contains(field)) { changed.set(index); } // The primary key's changed flag might've been set previously else if (changed.get(index)) { changed.set(index); } // [#2764] Users may override updatability of primary key values else if (updatablePrimaryKeys(settings(this))) { changed.set(index); } // [#2698] If the primary key has not yet been set else if (originals[index] == null) { changed.set(index); } // [#979] If the primary key is being changed, all other fields' flags // need to be set to true for in case this record is stored again, an // INSERT statement will thus be issued else { // [#945] Be sure that changed is never reset to false changed.set(index, changed.get(index) || !StringUtils.equals(values[index], value)); if (changed.get(index)) { changed(true); } } values[index] = value; } @Override public final <T, U> void set(Field<T> field, U value, Converter<? extends T, ? super U> converter) { set(field, converter.to(value)); } @Override public /* non-final */ <T> Record with(Field<T> field, T value) { set(field, value); return this; } @Override public <T, U> Record with(Field<T> field, U value, Converter<? extends T, ? super U> converter) { set(field, value, converter); return this; } final void setValues(Field<?>[] fields, AbstractRecord record) { fetched = record.fetched; for (Field<?> field : fields) { int targetIndex = indexOrFail(fieldsRow(), field); int sourceIndex = indexOrFail(record.fieldsRow(), field); values[targetIndex] = record.get(sourceIndex); originals[targetIndex] = record.original(sourceIndex); changed.set(targetIndex, record.changed(sourceIndex)); } } final void intern0(int fieldIndex) { safeIndex(fieldIndex); if (field(fieldIndex).getType() == String.class) { values[fieldIndex] = ((String) values[fieldIndex]).intern(); originals[fieldIndex] = ((String) originals[fieldIndex]).intern(); } } final int safeIndex(int index) { if (index >= 0 && index < values.length) return index; throw new IllegalArgumentException("No field at index " + index + " in Record type " + fieldsRow()); } /** * Subclasses may override this */ UniqueKey<?> getPrimaryKey() { return null; } /* * This method is overridden covariantly by TableRecordImpl */ @Override public Record original() { return Tools.newRecord(fetched, (Class<AbstractRecord>) getClass(), fields.fields.fields, configuration()) .operate(new RecordOperation<AbstractRecord, RuntimeException>() { @Override public AbstractRecord operate(AbstractRecord record) throws RuntimeException { for (int i = 0; i < originals.length; i++) { record.values[i] = originals[i]; record.originals[i] = originals[i]; } return record; } }); } @Override public final <T> T original(Field<T> field) { return (T) original(indexOrFail(fieldsRow(), field)); } @Override public final Object original(int fieldIndex) { return originals[safeIndex(fieldIndex)]; } @Override public final Object original(String fieldName) { return original(indexOrFail(fieldsRow(), fieldName)); } @Override public final Object original(Name fieldName) { return original(indexOrFail(fieldsRow(), fieldName)); } @Override public final boolean changed() { return !changed.isEmpty(); } @Override public final boolean changed(Field<?> field) { return changed(indexOrFail(fieldsRow(), field)); } @Override public final boolean changed(int fieldIndex) { return changed.get(safeIndex(fieldIndex)); } @Override public final boolean changed(String fieldName) { return changed(indexOrFail(fieldsRow(), fieldName)); } @Override public final boolean changed(Name fieldName) { return changed(indexOrFail(fieldsRow(), fieldName)); } @Override public final void changed(boolean c) { changed.set(0, values.length, c); // [#1995] If a value is meant to be "unchanged", the "original" should // match the supposedly "unchanged" value. if (!c) { System.arraycopy(values, 0, originals, 0, values.length); } } @Override public final void changed(Field<?> field, boolean c) { changed(indexOrFail(fieldsRow(), field), c); } @Override public final void changed(int fieldIndex, boolean c) { safeIndex(fieldIndex); changed.set(fieldIndex, c); // [#1995] If a value is meant to be "unchanged", the "original" should // match the supposedly "unchanged" value. if (!c) originals[fieldIndex] = values[fieldIndex]; } @Override public final void changed(String fieldName, boolean c) { changed(indexOrFail(fieldsRow(), fieldName), c); } @Override public final void changed(Name fieldName, boolean c) { changed(indexOrFail(fieldsRow(), fieldName), c); } @Override public final void reset() { changed.clear(); System.arraycopy(originals, 0, values, 0, originals.length); } @Override public final void reset(Field<?> field) { reset(indexOrFail(fieldsRow(), field)); } @Override public final void reset(int fieldIndex) { safeIndex(fieldIndex); changed.clear(fieldIndex); values[fieldIndex] = originals[fieldIndex]; } @Override public final void reset(String fieldName) { reset(indexOrFail(fieldsRow(), fieldName)); } @Override public final void reset(Name fieldName) { reset(indexOrFail(fieldsRow(), fieldName)); } @Override public final Object[] intoArray() { return into(Object[].class); } @Override public final List<Object> intoList() { return Arrays.asList(intoArray()); } @Override public final Stream<Object> intoStream() { return into(Stream.class); } @Override public final Map<String, Object> intoMap() { Map<String, Object> map = new LinkedHashMap<String, Object>(); int size = fields.size(); for (int i = 0; i < size; i++) { Field<?> field = fields.field(i); if (map.put(field.getName(), get(i)) != null) { throw new InvalidResultException("Field " + field.getName() + " is not unique in Record : " + this); } } return map; } @Override public final Record into(Field<?>... f) { return Tools.newRecord(fetched, Record.class, f, configuration()).operate(new TransferRecordState<Record>(f)); } // [jooq-tools] START [into-fields] @Override public final <T1> Record1<T1> into(Field<T1> field1) { return (Record1) into(new Field[] { field1 }); } @Override public final <T1, T2> Record2<T1, T2> into(Field<T1> field1, Field<T2> field2) { return (Record2) into(new Field[] { field1, field2 }); } @Override public final <T1, T2, T3> Record3<T1, T2, T3> into(Field<T1> field1, Field<T2> field2, Field<T3> field3) { return (Record3) into(new Field[] { field1, field2, field3 }); } @Override public final <T1, T2, T3, T4> Record4<T1, T2, T3, T4> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4) { return (Record4) into(new Field[] { field1, field2, field3, field4 }); } @Override public final <T1, T2, T3, T4, T5> Record5<T1, T2, T3, T4, T5> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5) { return (Record5) into(new Field[] { field1, field2, field3, field4, field5 }); } @Override public final <T1, T2, T3, T4, T5, T6> Record6<T1, T2, T3, T4, T5, T6> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6) { return (Record6) into(new Field[] { field1, field2, field3, field4, field5, field6 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7> Record7<T1, T2, T3, T4, T5, T6, T7> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7) { return (Record7) into(new Field[] { field1, field2, field3, field4, field5, field6, field7 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8> Record8<T1, T2, T3, T4, T5, T6, T7, T8> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8) { return (Record8) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9> Record9<T1, T2, T3, T4, T5, T6, T7, T8, T9> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9) { return (Record9) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Record10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10) { return (Record10) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Record11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11) { return (Record11) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Record12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12) { return (Record12) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Record13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13) { return (Record13) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Record14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14) { return (Record14) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> Record15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15) { return (Record15) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> Record16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16) { return (Record16) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17> Record17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17) { return (Record17) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18> Record18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17, Field<T18> field18) { return (Record18) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19> Record19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17, Field<T18> field18, Field<T19> field19) { return (Record19) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20> Record20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17, Field<T18> field18, Field<T19> field19, Field<T20> field20) { return (Record20) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21> Record21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17, Field<T18> field18, Field<T19> field19, Field<T20> field20, Field<T21> field21) { return (Record21) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21 }); } @Override public final <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> Record22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> into(Field<T1> field1, Field<T2> field2, Field<T3> field3, Field<T4> field4, Field<T5> field5, Field<T6> field6, Field<T7> field7, Field<T8> field8, Field<T9> field9, Field<T10> field10, Field<T11> field11, Field<T12> field12, Field<T13> field13, Field<T14> field14, Field<T15> field15, Field<T16> field16, Field<T17> field17, Field<T18> field18, Field<T19> field19, Field<T20> field20, Field<T21> field21, Field<T22> field22) { return (Record22) into(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22 }); } // [jooq-tools] END [into-fields] @Override public final <E> E into(Class<? extends E> type) { return (E) Tools.configuration(this).recordMapperProvider().provide(fields.fields, type).map(this); } @Override public final <E> E into(E object) { if (object == null) { throw new NullPointerException("Cannot copy Record into null"); } Class<E> type = (Class<E>) object.getClass(); try { return new DefaultRecordMapper<Record, E>(fields.fields, type, object, configuration()).map(this); } // Pass MappingExceptions on to client code catch (MappingException e) { throw e; } // All other reflection exceptions are intercepted catch (Exception e) { throw new MappingException("An error ocurred when mapping record to " + type, e); } } @Override public final <R extends Record> R into(Table<R> table) { return Tools.newRecord(fetched, table, configuration()).operate(new TransferRecordState<R>(table.fields())); } final <R extends Record> R intoRecord(Class<R> type) { return Tools.newRecord(fetched, type, fields(), configuration()).operate(new TransferRecordState<R>(null)); } private class TransferRecordState<R extends Record> implements RecordOperation<R, MappingException> { private final Field<?>[] targetFields; TransferRecordState(Field<?>[] targetFields) { this.targetFields = targetFields; } @Override public R operate(R target) throws MappingException { AbstractRecord source = AbstractRecord.this; try { // [#1522] [#2989] If possible the complete state of this record should be copied onto the other record if (target instanceof AbstractRecord) { AbstractRecord t = (AbstractRecord) target; // Iterate over target fields, to avoid ambiguities when two source fields share the same name. // [#3634] If external targetFields are provided, use those instead of the target record's fields. // The record doesn't know about aliased tables, for instance. for (int targetIndex = 0; targetIndex < (targetFields != null ? targetFields.length : t.size()); targetIndex++) { Field<?> targetField = (targetFields != null ? targetFields[targetIndex] : t.field(targetIndex)); int sourceIndex = fields.indexOf(targetField); if (sourceIndex >= 0) { DataType<?> targetType = targetField.getDataType(); t.values[targetIndex] = targetType.convert(values[sourceIndex]); t.originals[targetIndex] = targetType.convert(originals[sourceIndex]); t.changed.set(targetIndex, changed.get(sourceIndex)); } } } else { for (Field<?> targetField : target.fields()) { Field<?> sourceField = field(targetField); if (sourceField != null) { Tools.setValue(target, targetField, source, sourceField); } } } return target; } // All reflection exceptions are intercepted catch (Exception e) { throw new MappingException("An error ocurred when mapping record to " + target, e); } } } @Override public final ResultSet intoResultSet() { ResultImpl<Record> result = new ResultImpl<Record>(configuration(), fields.fields.fields); result.add(this); return result.intoResultSet(); } @Override public final <E> E map(RecordMapper<Record, E> mapper) { return mapper.map(this); } private final void from0(Object source, Fields f) { if (source == null) return; // [#2520] TODO: Benchmark this from() method. There's probably a better implementation from(Tools.configuration(this).recordUnmapperProvider().provide(source.getClass(), f).unmap(prepareArrayForUnmap(source, f))); // [#2700] [#3582] If a POJO attribute is NULL, but the column is NOT NULL // then we should let the database apply DEFAULT values resetChangedOnNotNull(this); } private final Object prepareArrayForUnmap(Object source, Fields f) { if (source instanceof Object[]) { Object[] array = (Object[]) source; if (array.length != f.size()) { Object[] result = new Object[f.size()]; for (int i = 0; i < result.length; i++) { int index = fields.indexOf(f.field(i)); result[i] = index >= 0 && index < array.length ? array[index] : null; } return result; } else return source; } else return source; } @Override public final void from(Object source) { from0(source, fields.fields); } @Override public final void from(Object source, Field<?>... f) { from0(source, new Fields(f)); } @Override public final void from(Object source, String... fieldNames) { from(source, fields(fieldNames)); } @Override public final void from(Object source, Name... fieldNames) { from(source, fields(fieldNames)); } @Override public final void from(Object source, int... fieldIndexes) { from(source, fields(fieldIndexes)); } @Override public final void fromMap(Map<String, ?> map) { from(map, fields()); } @Override public final void fromMap(Map<String, ?> map, Field<?>... f) { from0(map, new Fields(f)); } @Override public final void fromMap(Map<String, ?> map, String... fieldNames) { fromMap(map, fields(fieldNames)); } @Override public final void fromMap(Map<String, ?> map, Name... fieldNames) { fromMap(map, fields(fieldNames)); } @Override public final void fromMap(Map<String, ?> map, int... fieldIndexes) { fromMap(map, fields(fieldIndexes)); } @Override public final void fromArray(Object... array) { fromArray(array, fields()); } @Override public final void fromArray(Object[] array, Field<?>... f) { from0(array, new Fields(f)); } @Override public final void fromArray(Object[] array, String... fieldNames) { fromArray(array, fields(fieldNames)); } @Override public final void fromArray(Object[] array, Name... fieldNames) { fromArray(array, fields(fieldNames)); } @Override public final void fromArray(Object[] array, int... fieldIndexes) { fromArray(array, fields(fieldIndexes)); } /** * This method was implemented with [#799]. It may be useful to make it * public for broader use...? */ protected final void from(Record source) { for (Field<?> field : fields.fields.fields) { Field<?> sourceField = source.field(field); if (sourceField != null && source.changed(sourceField)) Tools.setValue(this, field, source, sourceField); } } // ------------------------------------------------------------------------- // Formatting methods // ------------------------------------------------------------------------- @Override public final String formatJSON() { StringWriter writer = new StringWriter(); formatJSON(writer); return writer.toString(); } @Override public final String formatJSON(JSONFormat format) { StringWriter writer = new StringWriter(); formatJSON(writer, format); return writer.toString(); } @Override public final void formatJSON(OutputStream stream) { formatJSON(new OutputStreamWriter(stream)); } @Override public final void formatJSON(OutputStream stream, JSONFormat format) { formatJSON(new OutputStreamWriter(stream), format); } @Override public final void formatJSON(Writer writer) { formatJSON(writer, JSONFormat.DEFAULT_FOR_RECORDS); } @Override public final void formatJSON(Writer writer, JSONFormat format) { if (format.header()) log.debug("JSONFormat.header currently not supported for Record.formatJSON()"); try { switch (format.recordFormat()) { case ARRAY: ResultImpl.formatJSONArray0(this, fields.fields, format, 0, writer); break; case OBJECT: ResultImpl.formatJSONMap0(this, fields.fields, format, 0, writer); break; default: throw new IllegalArgumentException("Format not supported: " + format); } } catch (java.io.IOException e) { throw new IOException("Exception while writing JSON", e); } } @Override public final String formatXML() { return formatXML(XMLFormat.DEFAULT_FOR_RECORDS); } @Override public final String formatXML(XMLFormat format) { StringWriter writer = new StringWriter(); formatXML(writer, format); return writer.toString(); } @Override public final void formatXML(OutputStream stream) { formatXML(stream, XMLFormat.DEFAULT_FOR_RECORDS); } @Override public final void formatXML(OutputStream stream, XMLFormat format) { formatXML(new OutputStreamWriter(stream), format); } @Override public final void formatXML(Writer writer) { formatXML(writer, XMLFormat.DEFAULT_FOR_RECORDS); } @Override public final void formatXML(Writer writer, XMLFormat format) { if (format.header()) log.debug("XMLFormat.header currently not supported for Record.formatXML()"); try { ResultImpl.formatXMLRecord(writer, format, 0, this, fields.fields); } catch (java.io.IOException e) { throw new IOException("Exception while writing XML", e); } } // ------------------------------------------------------------------------ // XXX: Object and Comparable API // ------------------------------------------------------------------------ @Override public String toString() { // [#3900] Nested records should generate different toString() behaviour return ThreadGuard.run(RECORD_TOSTRING, new GuardedOperation<String>() { @Override public String unguarded() { Result<AbstractRecord> result = new ResultImpl<AbstractRecord>(configuration(), fields.fields.fields); result.add(AbstractRecord.this); return result.toString(); } @Override public String guarded() { return valuesRow().toString(); } }); } @Override public int compareTo(Record that) { // Note: keep this implementation in-sync with AbstractStore.equals()! if (that == null) { throw new NullPointerException(); } if (size() != that.size()) { throw new ClassCastException(String.format("Trying to compare incomparable records (wrong degree):\n%s\n%s", this, that)); } Class<?>[] thisTypes = this.fieldsRow().types(); Class<?>[] thatTypes = that.fieldsRow().types(); if (!asList(thisTypes).equals(asList(thatTypes))) { throw new ClassCastException(String.format("Trying to compare incomparable records (type mismatch):\n%s\n%s", this, that)); } for (int i = 0; i < size(); i++) { final Object thisValue = get(i); final Object thatValue = that.get(i); // [#1850] Only return -1/+1 early. In all other cases, // continue checking the remaining fields if (thisValue == null && thatValue == null) { continue; } // Order column values in a SQL NULLS LAST manner else if (thisValue == null) { return 1; } else if (thatValue == null) { return -1; } // [#985] Compare arrays too. else if (thisValue.getClass().isArray() && thatValue.getClass().isArray()) { // Might be byte[] if (thisValue.getClass() == byte[].class) { int compare = compare((byte[]) thisValue, (byte[]) thatValue); if (compare != 0) { return compare; } } // Other primitive types are not expected else if (!thisValue.getClass().getComponentType().isPrimitive()) { int compare = compare((Object[]) thisValue, (Object[]) thatValue); if (compare != 0) { return compare; } } else { throw new ClassCastException(String.format("Unsupported data type in natural ordering: %s", thisValue.getClass())); } } else { int compare = ((Comparable) thisValue).compareTo(thatValue); if (compare != 0) { return compare; } } } // If we got through the above loop, the two records are equal return 0; } /** * Compare two byte arrays */ final int compare(byte[] array1, byte[] array2) { int length = Math.min(array1.length, array2.length); for (int i = 0; i < length; i++) { int v1 = (array1[i] & 0xff); int v2 = (array2[i] & 0xff); if (v1 != v2) { return v1 < v2 ? -1 : 1; } } return array1.length - array2.length; } /** * Compare two arrays */ final int compare(Object[] array1, Object[] array2) { int length = Math.min(array1.length, array2.length); for (int i = 0; i < length; i++) { int compare = ((Comparable) array1[i]).compareTo(array2[i]); if (compare != 0) { return compare; } } return array1.length - array2.length; } // ------------------------------------------------------------------------- // XXX: Deprecated and discouraged methods // ------------------------------------------------------------------------- @Override public final <T> T getValue(Field<T> field) { return get(field); } @Override @Deprecated public final <T> T getValue(Field<T> field, T defaultValue) { T result = getValue(field); return result != null ? result : defaultValue; } @Override public final <T> T getValue(Field<?> field, Class<? extends T> type) { return get(field, type); } @Override @Deprecated public final <T> T getValue(Field<?> field, Class<? extends T> type, T defaultValue) { final T result = get(field, type); return result == null ? defaultValue : result; } @Override public final <T, U> U getValue(Field<T> field, Converter<? super T, ? extends U> converter) { return get(field, converter); } @Override @Deprecated public final <T, U> U getValue(Field<T> field, Converter<? super T, ? extends U> converter, U defaultValue) { final U result = get(field, converter); return result == null ? defaultValue : result; } @Override public final Object getValue(int index) { return get(index); } @Override @Deprecated public final Object getValue(int index, Object defaultValue) { final Object result = get(index); return result == null ? defaultValue : result; } @Override public final <T> T getValue(int index, Class<? extends T> type) { return get(index, type); } @Override @Deprecated public final <T> T getValue(int index, Class<? extends T> type, T defaultValue) { final T result = get(index, type); return result == null ? defaultValue : result; } @Override public final <U> U getValue(int index, Converter<?, ? extends U> converter) { return get(index, converter); } @Override @Deprecated public final <U> U getValue(int index, Converter<?, ? extends U> converter, U defaultValue) { final U result = get(index, converter); return result == null ? defaultValue : result; } @Override public final Object getValue(String fieldName) { return get(fieldName); } @Override @Deprecated public final Object getValue(String fieldName, Object defaultValue) { return getValue(indexOrFail(fieldsRow(), fieldName), defaultValue); } @Override public final <T> T getValue(String fieldName, Class<? extends T> type) { return get(fieldName, type); } @Override @Deprecated public final <T> T getValue(String fieldName, Class<? extends T> type, T defaultValue) { final T result = get(fieldName, type); return result == null ? defaultValue : result; } @Override public final <U> U getValue(String fieldName, Converter<?, ? extends U> converter) { return get(fieldName, converter); } @Override @Deprecated public final <U> U getValue(String fieldName, Converter<?, ? extends U> converter, U defaultValue) { final U result = get(fieldName, converter); return result == null ? defaultValue : result; } @Override public final Object getValue(Name fieldName) { return get(fieldName); } @Override public final <T> T getValue(Name fieldName, Class<? extends T> type) { return get(fieldName, type); } @Override public final <U> U getValue(Name fieldName, Converter<?, ? extends U> converter) { return get(fieldName, converter); } @Override public final <T> void setValue(Field<T> field, T value) { set(field, value); } @Override public final <T, U> void setValue(Field<T> field, U value, Converter<? extends T, ? super U> converter) { set(field, value, converter); } }