/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.avro.generic; import java.io.IOException; import org.apache.avro.AvroRuntimeException; import org.apache.avro.Schema; import org.apache.avro.Schema.Field; import org.apache.avro.data.RecordBuilderBase; import org.apache.avro.generic.GenericData.Record; /** A RecordBuilder for generic records. GenericRecordBuilder fills in default values * for fields if they are not specified. */ public class GenericRecordBuilder extends RecordBuilderBase<Record> { private final GenericData.Record record; /** * Creates a GenericRecordBuilder for building Record instances. * @param schema the schema associated with the record class. */ public GenericRecordBuilder(Schema schema) { super(schema, GenericData.get()); record = new GenericData.Record(schema); } /** * Creates a GenericRecordBuilder by copying an existing GenericRecordBuilder. * @param other the GenericRecordBuilder to copy. */ public GenericRecordBuilder(GenericRecordBuilder other) { super(other, GenericData.get()); record = new GenericData.Record(other.record, /* deepCopy = */ true); } /** * Creates a GenericRecordBuilder by copying an existing record instance. * @param other the record instance to copy. */ public GenericRecordBuilder(Record other) { super(other.getSchema(), GenericData.get()); record = new GenericData.Record(other, /* deepCopy = */ true); // Set all fields in the RecordBuilder that are set in the record for (Field f : schema().getFields()) { Object value = other.get(f.pos()); // Only set the value if it is not null, if the schema type is null, // or if the schema type is a union that accepts nulls. if (isValidValue(f, value)) { set(f, data().deepCopy(f.schema(), value)); } } } /** * Gets the value of a field. * @param fieldName the name of the field to get. * @return the value of the field with the given name, or null if not set. */ public Object get(String fieldName) { return get(schema().getField(fieldName)); } /** * Gets the value of a field. * @param field the field to get. * @return the value of the given field, or null if not set. */ public Object get(Field field) { return get(field.pos()); } /** * Gets the value of a field. * @param pos the position of the field to get. * @return the value of the field with the given position, or null if not set. */ protected Object get(int pos) { return record.get(pos); } /** * Sets the value of a field. * @param fieldName the name of the field to set. * @param value the value to set. * @return a reference to the RecordBuilder. */ public GenericRecordBuilder set(String fieldName, Object value) { return set(schema().getField(fieldName), value); } /** * Sets the value of a field. * @param field the field to set. * @param value the value to set. * @return a reference to the RecordBuilder. */ public GenericRecordBuilder set(Field field, Object value) { return set(field, field.pos(), value); } /** * Sets the value of a field. * @param pos the field to set. * @param value the value to set. * @return a reference to the RecordBuilder. */ protected GenericRecordBuilder set(int pos, Object value) { return set(fields()[pos], pos, value); } /** * Sets the value of a field. * @param field the field to set. * @param pos the position of the field. * @param value the value to set. * @return a reference to the RecordBuilder. */ private GenericRecordBuilder set(Field field, int pos, Object value) { validate(field, value); record.put(pos, value); fieldSetFlags()[pos] = true; return this; } /** * Checks whether a field has been set. * @param fieldName the name of the field to check. * @return true if the given field is non-null; false otherwise. */ public boolean has(String fieldName) { return has(schema().getField(fieldName)); } /** * Checks whether a field has been set. * @param field the field to check. * @return true if the given field is non-null; false otherwise. */ public boolean has(Field field) { return has(field.pos()); } /** * Checks whether a field has been set. * @param pos the position of the field to check. * @return true if the given field is non-null; false otherwise. */ protected boolean has(int pos) { return fieldSetFlags()[pos]; } /** * Clears the value of the given field. * @param fieldName the name of the field to clear. * @return a reference to the RecordBuilder. */ public GenericRecordBuilder clear(String fieldName) { return clear(schema().getField(fieldName)); } /** * Clears the value of the given field. * @param field the field to clear. * @return a reference to the RecordBuilder. */ public GenericRecordBuilder clear(Field field) { return clear(field.pos()); } /** * Clears the value of the given field. * @param pos the position of the field to clear. * @return a reference to the RecordBuilder. */ protected GenericRecordBuilder clear(int pos) { record.put(pos, null); fieldSetFlags()[pos] = false; return this; } @Override public Record build() { Record record; try { record = new GenericData.Record(schema()); } catch (Exception e) { throw new AvroRuntimeException(e); } for (Field field : fields()) { Object value; try { value = getWithDefault(field); } catch(IOException e) { throw new AvroRuntimeException(e); } if (value != null) { record.put(field.pos(), value); } } return record; } /** * Gets the value of the given field. * If the field has been set, the set value is returned (even if it's null). * If the field hasn't been set and has a default value, the default value * is returned. * @param field the field whose value should be retrieved. * @return the value set for the given field, the field's default value, * or null. * @throws IOException */ private Object getWithDefault(Field field) throws IOException { return fieldSetFlags()[field.pos()] ? record.get(field.pos()) : defaultValue(field); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((record == null) ? 0 : record.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; GenericRecordBuilder other = (GenericRecordBuilder) obj; if (record == null) { if (other.record != null) return false; } else if (!record.equals(other.record)) return false; return true; } }