/* * 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.kafka.common.protocol.types; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; /** * The schema for a compound record definition */ public class Schema extends Type { private final Field[] fields; private final Map<String, Field> fieldsByName; /** * Construct the schema with a given list of its field values * * @throws SchemaException If the given list have duplicate fields */ public Schema(Field... fs) { this.fields = new Field[fs.length]; this.fieldsByName = new HashMap<>(); for (int i = 0; i < this.fields.length; i++) { Field field = fs[i]; if (fieldsByName.containsKey(field.name)) throw new SchemaException("Schema contains a duplicate field: " + field.name); this.fields[i] = new Field(i, field.name, field.type, field.doc, field.defaultValue, this); this.fieldsByName.put(fs[i].name, this.fields[i]); } } /** * Write a struct to the buffer */ @Override public void write(ByteBuffer buffer, Object o) { Struct r = (Struct) o; for (Field field : fields) { try { Object value = field.type().validate(r.get(field)); field.type.write(buffer, value); } catch (Exception e) { throw new SchemaException("Error writing field '" + field.name + "': " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage())); } } } /** * Read a struct from the buffer */ @Override public Struct read(ByteBuffer buffer) { Object[] objects = new Object[fields.length]; for (int i = 0; i < fields.length; i++) { try { objects[i] = fields[i].type.read(buffer); } catch (Exception e) { throw new SchemaException("Error reading field '" + fields[i].name + "': " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage())); } } return new Struct(this, objects); } /** * The size of the given record */ @Override public int sizeOf(Object o) { int size = 0; Struct r = (Struct) o; for (Field field : fields) { try { size += field.type.sizeOf(r.get(field)); } catch (Exception e) { throw new SchemaException("Error computing size for field '" + field.name + "': " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage())); } } return size; } /** * The number of fields in this schema */ public int numFields() { return this.fields.length; } /** * Get a field by its slot in the record array * * @param slot The slot at which this field sits * @return The field */ public Field get(int slot) { return this.fields[slot]; } /** * Get a field by its name * * @param name The name of the field * @return The field */ public Field get(String name) { return this.fieldsByName.get(name); } /** * Get all the fields in this schema */ public Field[] fields() { return this.fields; } /** * Display a string representation of the schema */ @Override public String toString() { StringBuilder b = new StringBuilder(); b.append('{'); for (int i = 0; i < this.fields.length; i++) { b.append(this.fields[i].name); b.append(':'); b.append(this.fields[i].type()); if (i < this.fields.length - 1) b.append(','); } b.append("}"); return b.toString(); } @Override public Struct validate(Object item) { try { Struct struct = (Struct) item; for (Field field : fields) { try { field.type.validate(struct.get(field)); } catch (SchemaException e) { throw new SchemaException("Invalid value for field '" + field.name + "': " + e.getMessage()); } } return struct; } catch (ClassCastException e) { throw new SchemaException("Not a Struct."); } } }