001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.data.id; 019 020import com.google.common.collect.ImmutableClassToInstanceMap; 021import com.google.common.collect.ImmutableList; 022 023import dev.enola.common.convert.ConversionException; 024 025import org.jspecify.annotations.NonNull; 026 027import java.io.IOException; 028import java.util.Optional; 029 030/** 031 * IdConverterChain is an {@link IdConverter} which delegates to a list of other such converters. 032 */ 033public class IdConverterChain implements IdConverter<Object> { 034 035 private final ImmutableList<IdConverter<?>> converters; 036 private final ImmutableClassToInstanceMap<IdConverter<?>> convertersMap; 037 038 public IdConverterChain(IdConverter<?>... converters) { 039 this(ImmutableList.copyOf(converters)); 040 } 041 042 public IdConverterChain(Iterable<IdConverter<?>> converters) { 043 this.converters = ImmutableList.copyOf(converters); 044 this.convertersMap = map(converters); 045 } 046 047 @SuppressWarnings({"rawtypes", "unchecked"}) 048 private static ImmutableClassToInstanceMap<IdConverter<?>> map( 049 Iterable<IdConverter<?>> converters) { 050 var convertersMapBuilder = ImmutableClassToInstanceMap.<IdConverter<?>>builder(); 051 for (IdConverter converter : converters) 052 convertersMapBuilder.put(converter.idClass(), converter); 053 return convertersMapBuilder.build(); 054 } 055 056 @Override 057 @SuppressWarnings("unchecked") 058 public boolean convertInto(Object from, Appendable into) 059 throws ConversionException, IOException { 060 Class<?> idClass = from.getClass(); 061 IdConverter<Object> converter = (IdConverter<Object>) convertersMap.get(idClass); 062 if (converter == null) 063 throw new IllegalStateException("No IdConverter registered for " + idClass); 064 return converter.convertInto(from, into); 065 } 066 067 @Override 068 @SuppressWarnings("unchecked") 069 public @NonNull String convertTo(@NonNull Object input) throws ConversionException { 070 Class<?> idClass = input.getClass(); 071 IdConverter<Object> converter = (IdConverter<Object>) convertersMap.get(idClass); 072 if (converter == null) 073 throw new IllegalStateException("No IdConverter registered for " + idClass); 074 return converter.convertTo(input); 075 } 076 077 @Override 078 @SuppressWarnings("unchecked") 079 public Optional<Object> convert(String input) throws ConversionException { 080 for (IdConverter<?> converter : converters) { 081 var opt = converter.convert(input); 082 if (opt.isPresent()) return (Optional<Object>) opt; 083 } 084 return Optional.empty(); 085 } 086 087 @Override 088 public Class<Object> idClass() { 089 return Object.class; 090 } 091}