/* * Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.data.record; import java.util.Collection; import java.util.Objects; /** * Static utility methods for working with {@link Record records}. * * @author pron */ public final class Records { /** * Creates a record object that delegates all operations to the given record. * * @param owner any object that can identify the owner of the newly created record and can be used to restrict modification of the delegate * @param record the record to which operations will be delegated * @return a new record object that delegates all operations to the given record. * @see #setDelegateTarget(Record, Object, Record) */ public static <R> Record<R> delegate(Object owner, Record<R> record) { return new RecordDelegate<R>(owner, record); } /** * Sets the target record of a record created with {@link #delegate(Object, Record) delegate()}. * * @param record the delegate record returned from {@link #delegate(Object, Record) delegate()}. * @param owner the owner object passed to {@link #delegate(Object, Record) delegate()}. * @param newDelegate the new target */ public static <R> void setDelegateTarget(Record<R> record, Object owner, Record<R> newDelegate) { if (!(record instanceof RecordDelegate)) throw new UnsupportedOperationException("Record " + record + " is not a record delegate"); ((RecordDelegate<R>) record).setDelegate(owner, newDelegate); } /** * Returns the target record of a record created with {@link #delegate(Object, Record) delegate()}. * * @param record the delegate record returned from {@link #delegate(Object, Record) delegate()}. * @param owner the owner object passed to {@link #delegate(Object, Record) delegate()}. * @return the target of the delegate record */ public static <R> Record<R> getDelegateTarget(Record<R> record, Object owner) { if (!(record instanceof RecordDelegate)) return record; return ((RecordDelegate<R>) record).getDelegate(owner); } /** * Copies the contents (all fields) of one record into another, including transient fields. * * @param source the source record * @param target the target record */ public static <R> void copy(Record<R> source, Record<R> target) { copy(source, target, true); } /** * Copies the contents (all fields) of one record into another. * * @param source the source record * @param target the target record * @param copyTransient whether to copy transient fields */ public static <R> void copy(Record<R> source, Record<R> target, boolean copyTransient) { copy(source, target, target.fields()); } /** * Copies specified fields of one record into another. * * @param source the source record * @param target the target record * @param fields the fields to copy */ public static <R> void copy(Record<R> source, Record<R> target, Collection<Field<? super R, ?>> fields) { copy(source, target, fields, true); } public static <R> void copy(Record<R> source, Record<R> target, Collection<Field<? super R, ?>> fields, boolean copyTransient) { for (Field<? super R, ?> field : fields) { if (!copyTransient && field.isTransient()) continue; try { switch (field.type()) { case Field.BOOLEAN: target.set((Field.BooleanField) field, source.get((Field.BooleanField) field)); break; case Field.BYTE: target.set((Field.ByteField) field, source.get((Field.ByteField) field)); break; case Field.SHORT: target.set((Field.ShortField) field, source.get((Field.ShortField) field)); break; case Field.INT: target.set((Field.IntField) field, source.get((Field.IntField) field)); break; case Field.LONG: target.set((Field.LongField) field, source.get((Field.LongField) field)); break; case Field.FLOAT: target.set((Field.FloatField) field, source.get((Field.FloatField) field)); break; case Field.DOUBLE: target.set((Field.DoubleField) field, source.get((Field.DoubleField) field)); break; case Field.CHAR: target.set((Field.CharField) field, source.get((Field.CharField) field)); break; case Field.OBJECT: target.set((Field.ObjectField) field, source.get((Field.ObjectField) field)); break; case Field.BOOLEAN_ARRAY: target.set((Field.BooleanArrayField) field, source, (Field.BooleanArrayField) field); break; case Field.BYTE_ARRAY: target.set((Field.ByteArrayField) field, source, (Field.ByteArrayField) field); break; case Field.SHORT_ARRAY: target.set((Field.ShortArrayField) field, source, (Field.ShortArrayField) field); break; case Field.INT_ARRAY: target.set((Field.IntArrayField) field, source, (Field.IntArrayField) field); break; case Field.LONG_ARRAY: target.set((Field.LongArrayField) field, source, (Field.LongArrayField) field); break; case Field.FLOAT_ARRAY: target.set((Field.FloatArrayField) field, source, (Field.FloatArrayField) field); break; case Field.DOUBLE_ARRAY: target.set((Field.DoubleArrayField) field, source, (Field.DoubleArrayField) field); break; case Field.CHAR_ARRAY: target.set((Field.CharArrayField) field, source, (Field.CharArrayField) field); break; case Field.OBJECT_ARRAY: target.set((Field.ObjectArrayField) field, source, (Field.ObjectArrayField) field); break; default: throw new AssertionError(); } } catch (FieldNotFoundException e) { } } } /** * Creates a shallow clone of the given record. * * @param <R> * @param source the record to clone */ public static <R> Record<R> clone(Record<R> source) { return clone(source, true); } /** * Creates a shallow clone of the given record, optionally not copying transient fields. * * @param <R> * @param source the record to clone * @param copyTransients whether to copy transient fields */ public static <R> Record<R> clone(Record<R> source, boolean copyTransients) { final Record<R> target = source.type().newInstance(); copy(source, target, copyTransients); return target; } /** * Clears a record. Sets all object fields and object array elements to {@code null} * and all primitive fields and primitive array elements to {@code 0}. * * @param record the record */ public static <R> void clear(Record<R> record) { for (Field<? super R, ?> field : record.fields()) { try { switch (field.type()) { case Field.BOOLEAN: record.set((Field.BooleanField) field, false); break; case Field.BYTE: record.set((Field.ByteField) field, (byte) 0); break; case Field.SHORT: record.set((Field.ShortField) field, (short) 0); break; case Field.INT: record.set((Field.IntField) field, 0); break; case Field.LONG: record.set((Field.LongField) field, 0L); break; case Field.FLOAT: record.set((Field.FloatField) field, 0.0f); break; case Field.DOUBLE: record.set((Field.DoubleField) field, 0.0); break; case Field.CHAR: record.set((Field.CharField) field, (char) 0); break; case Field.OBJECT: record.set((Field.ObjectField) field, null); break; case Field.BOOLEAN_ARRAY: { Field.BooleanArrayField f = (Field.BooleanArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, false); break; } case Field.BYTE_ARRAY: { Field.ByteArrayField f = (Field.ByteArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, (byte) 0); break; } case Field.SHORT_ARRAY: { Field.ShortArrayField f = (Field.ShortArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, (short) 0); break; } case Field.INT_ARRAY: { Field.IntArrayField f = (Field.IntArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, 0); break; } case Field.LONG_ARRAY: { Field.LongArrayField f = (Field.LongArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, 0L); break; } case Field.FLOAT_ARRAY: { Field.FloatArrayField f = (Field.FloatArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, 0.0f); break; } case Field.DOUBLE_ARRAY: { Field.DoubleArrayField f = (Field.DoubleArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, 0.0); break; } case Field.CHAR_ARRAY: { Field.CharArrayField f = (Field.CharArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, (char) 0); break; } case Field.OBJECT_ARRAY: { Field.ObjectArrayField f = (Field.ObjectArrayField) field; for (int i = 0; i < f.length; i++) record.set(f, i, null); break; } default: throw new AssertionError(); } } catch (FieldNotFoundException e) { } } } /** * Clears all elements of a {@link RecordArray} as in {@link #clear(Record)}. * * @param recordArray the {@link RecordArray} */ public static <R> void clear(RecordArray<R> recordArray) { if (recordArray instanceof SimpleRecordArray) { ((SimpleRecordArray<R>) recordArray).clear(); } else throw new UnsupportedOperationException(); } /** * Perform a deep {@code equals} on two records. * * @param a the first record * @param b the second record * @return {@code true} if all fields of record {@code a} are recursively equal to the respective fields of record {@code b}. */ public static <R> boolean deepEquals(Record<R> a, Record<R> b) { if (a == b) return true; if (a == null | b == null) return false; if (!a.fields().equals(b.fields())) return false; for (Field<? super R, ?> field : a.fields()) { try { switch (field.type()) { case Field.BOOLEAN: if (a.get((Field.BooleanField) field) != b.get((Field.BooleanField) field)) return false; break; case Field.BYTE: if (a.get((Field.ByteField) field) != b.get((Field.ByteField) field)) return false; break; case Field.SHORT: if (a.get((Field.ShortField) field) != b.get((Field.ShortField) field)) return false; break; case Field.INT: if (a.get((Field.IntField) field) != b.get((Field.IntField) field)) return false; break; case Field.LONG: if (a.get((Field.LongField) field) != b.get((Field.LongField) field)) return false; break; case Field.FLOAT: if (a.get((Field.FloatField) field) != b.get((Field.FloatField) field)) return false; break; case Field.DOUBLE: if (a.get((Field.DoubleField) field) != b.get((Field.DoubleField) field)) return false; break; case Field.CHAR: if (a.get((Field.CharField) field) != b.get((Field.CharField) field)) return false; break; case Field.OBJECT: if (!(Objects.equals(a.get((Field.ObjectField) field), b.get((Field.ObjectField) field)))) return false; break; case Field.BOOLEAN_ARRAY: { Field.BooleanArrayField f = (Field.BooleanArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.BYTE_ARRAY: { Field.ByteArrayField f = (Field.ByteArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.SHORT_ARRAY: { Field.ShortArrayField f = (Field.ShortArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.INT_ARRAY: { Field.IntArrayField f = (Field.IntArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.LONG_ARRAY: { Field.LongArrayField f = (Field.LongArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.FLOAT_ARRAY: { Field.FloatArrayField f = (Field.FloatArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.DOUBLE_ARRAY: { Field.DoubleArrayField f = (Field.DoubleArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.CHAR_ARRAY: { Field.CharArrayField f = (Field.CharArrayField) field; for (int i = 0; i < f.length; i++) { if (a.get(f, i) != b.get(f, i)) return false; } break; } case Field.OBJECT_ARRAY: { Field.ObjectArrayField f = (Field.ObjectArrayField) field; for (int i = 0; i < f.length; i++) { if (Objects.equals(a.get(f, i), b.get(f, i))) return false; } break; } default: throw new AssertionError(); } } catch (FieldNotFoundException e) { return false; } } return true; } private Records() { } }