001/* 002 * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * pierre 018 */ 019package org.nuxeo.runtime.avro; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Objects; 029 030import org.apache.avro.Schema; 031 032/** 033 * An AvroSchemaFactoryContext represents a context in which Avro schemas are cached and reused depending on their 034 * qualified name.<br> 035 * <br> 036 * Avro does not permit to declare twice a schema with the same qualified name. Thus a schema has to be fully described 037 * the first time it appears in the object, and then be referred by name.<br> 038 * 039 * @since 10.2 040 */ 041public class AvroSchemaFactoryContext { 042 043 protected static final AvroSchemaFactory<Object> NULL = new AvroSchemaFactory<Object>(null) { 044 045 @Override 046 public Schema createSchema(Object input) { 047 return null; 048 } 049 050 @Override 051 public String getName(Object input) { 052 return null; 053 } 054 }; 055 056 protected final Map<Class<?>, AvroSchemaFactory<?>> factories = new HashMap<>(); 057 058 protected final Map<String, Schema> createdSchemas = new HashMap<>(); 059 060 protected final AvroService service; 061 062 protected AvroSchemaFactoryContext(AvroService service) { 063 super(); 064 this.service = service; 065 } 066 067 public <T> Schema createSchema(T input) { 068 String qualifiedName = getFactory(input).getQualifiedName(input); 069 return createdSchemas.computeIfAbsent(qualifiedName, k -> getFactory(input).createSchema(input)); 070 } 071 072 public AvroService getService() { 073 return service; 074 } 075 076 public <U> List<U> sort(Collection<U> children) { 077 if (children == null || children.isEmpty()) { 078 return Collections.emptyList(); 079 } 080 AvroSchemaFactory<U> factory = getFactory(children.iterator().next()); 081 if (factory == null) { 082 return Collections.emptyList(); 083 } 084 List<U> sortedChildren = new ArrayList<>(children); 085 sortedChildren.sort((o1, o2) -> Objects.compare(factory.getQualifiedName(o1), factory.getQualifiedName(o2), 086 String::compareTo)); 087 return sortedChildren; 088 } 089 090 @SuppressWarnings("unchecked") 091 protected <T> AvroSchemaFactory<T> getFactory(T input) { 092 AvroSchemaFactory<T> factory = (AvroSchemaFactory<T>) factories.get(input.getClass()); 093 if (factory != null) { 094 return factory; 095 } 096 for (Class<?> intrface : input.getClass().getInterfaces()) { 097 factory = (AvroSchemaFactory<T>) factories.get(intrface); 098 if (factory != null) { 099 return factory; 100 } 101 } 102 for (Entry<Class<?>, AvroSchemaFactory<?>> entry : factories.entrySet()) { 103 if (entry.getKey().isAssignableFrom(input.getClass())) { 104 return (AvroSchemaFactory<T>) entry.getValue(); 105 } 106 } 107 return (AvroSchemaFactory<T>) NULL; 108 } 109 110 protected void register(Class<?> type, AvroSchemaFactory<?> factory) { 111 factories.put(type, factory); 112 } 113 114}