/* * 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. */ package org.rakam.util; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.avro.Schema; import org.codehaus.jackson.node.NullNode; import org.rakam.collection.FieldType; import org.rakam.collection.SchemaField; import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.avro.Schema.Type.NULL; public final class AvroUtil { static { try { Field validateNames = Schema.class.getDeclaredField("validateNames"); boolean accessible = validateNames.isAccessible(); validateNames.setAccessible(true); validateNames.set(null, new ThreadLocal<Boolean>() { @Override public Boolean get() { return false; } @Override public void set(Boolean value) { // no-op return; } }); if(!accessible) { validateNames.setAccessible(false); } } catch (NoSuchFieldException|IllegalAccessException e) { throw Throwables.propagate(e); } } private AvroUtil() throws InstantiationException { throw new InstantiationException("The class is not created for instantiation"); } public static Schema convertAvroSchema(List<SchemaField> fields, Map<String, List<SchemaField>> conditionalMagicFields) { List<Schema.Field> avroFields = fields.stream() .map(AvroUtil::generateAvroField).collect(Collectors.toList()); Schema schema = Schema.createRecord("collection", null, null, false); conditionalMagicFields.keySet().stream() .filter(s -> !avroFields.stream().anyMatch(af -> af.name().equals(s))) .map(n -> new Schema.Field(n, Schema.create(NULL), "", null)) .forEach(x -> avroFields.add(x)); schema.setFields(avroFields); return schema; } public static Schema convertAvroSchema(Collection<SchemaField> fields) { List<Schema.Field> avroFields = fields.stream() .map(AvroUtil::generateAvroField).collect(Collectors.toList()); Schema schema = Schema.createRecord("collection", null, null, false); schema.setFields(avroFields); return schema; } public static Schema.Field generateAvroField(SchemaField field) { return new Schema.Field(field.getName(), generateAvroSchema(field.getType()), null, NullNode.getInstance()); } public static Schema generateAvroSchema(FieldType field) { return Schema.createUnion(Lists.newArrayList(Schema.create(NULL), getAvroSchema(field))); } public static Schema getAvroSchema(FieldType type) { switch (type) { case STRING: return Schema.create(Schema.Type.STRING); case BINARY: return Schema.create(Schema.Type.BYTES); case DOUBLE: case DECIMAL: return Schema.create(Schema.Type.DOUBLE); case BOOLEAN: return Schema.create(Schema.Type.BOOLEAN); case DATE: case TIME: case INTEGER: return Schema.create(Schema.Type.INT); case LONG: case TIMESTAMP: return Schema.create(Schema.Type.LONG); default: if (type.isMap()) { Schema union = Schema.createUnion(ImmutableList.of(Schema.create(Schema.Type.NULL), getAvroSchema(type.getMapValueType()))); return Schema.createMap(union); } if (type.isArray()) { Schema union = Schema.createUnion(ImmutableList.of(Schema.create(Schema.Type.NULL), getAvroSchema(type.getArrayElementType()))); return Schema.createArray(union); } throw new IllegalStateException(); } } }