All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.encoding.DefaultEncodingRegistry Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.encoding;

import com.google.common.base.Preconditions;
import com.google.common.reflect.TypeToken;

import java.io.File;
import java.net.URI;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Permazen's default {@link EncodingRegistry}.
 *
 * 

* Instances automatically register all of Permazen's built-in {@link Encoding}s (see below). * *

Array Types

* *

* Because this class is a subclass of {@link SimpleEncodingRegistry}, encodings for array types * (other than primitive arrays) are created on demand, so they don't need to be explicitly registered. * *

Enum Types

* *

* Encodings for {@link Enum} types are not registered in an {@link EncodingRegistry}. Instead, {@link Enum} * values are represented as {@link io.permazen.core.EnumValue} instances, and {@link Enum} fields are specially * defined in the schema with an explicit identifier list. In turn, {@link io.permazen.core.EnumValue}'s are * encoded by {@link io.permazen.core.EnumValueEncoding}. * *

Custom Encodings

* *

* During construction, instances scan the class path for custom {@link EncodingRegistry} implementations * and will delegate to them when an encoding is not found. Custom {@link EncodingRegistry} implementations are specified via * {@code META-INF/services/io.permazen.encoding.EncodingRegistry} files or by module exports; see {@link ServiceLoader}. * Custom implementations are only queried for non-array types. * *

* If multiple custom {@link EncodingRegistry} implementations advertise the same encoding, one will be * chosen arbitrarily. * *

Built-in Encodings

* *

* Permazen provides built-in {@link Encoding}s covering the following Java types: *

    *
  • Primitive types ({@code boolean}, {@code int}, etc.)
  • *
  • Primitive array types ({@code boolean[]}, {@code int[]}, etc.)
  • *
  • Primitive wrapper types ({@link Boolean}, {@link Integer}, etc.)
  • *
  • {@link String}
  • *
  • {@link java.math.BigDecimal}
  • *
  • {@link java.math.BigInteger}
  • *
  • {@link java.util.BitSet}
  • *
  • {@link java.util.Date}
  • *
  • {@link java.util.UUID}
  • *
  • {@link java.util.IntSummaryStatistics}
  • *
  • {@link java.util.LongSummaryStatistics}
  • *
  • {@link java.util.DoubleSummaryStatistics}
  • *
  • {@link java.net.URI}
  • *
  • {@link java.io.File}
  • *
  • {@link java.net.InetAddress}
  • *
  • {@link java.net.Inet4Address}
  • *
  • {@link java.net.Inet6Address}
  • *
  • {@link java.util.regex.Pattern}
  • *
  • {@link java.time java.time.*}
  • *
  • {@link jakarta.mail.internet.InternetAddress} (if present on the classpath)
  • *
*/ public class DefaultEncodingRegistry extends SimpleEncodingRegistry { protected final ArrayList customEncodingRegistries = new ArrayList<>(); // Constructor /** * Constructor. * *

* This constructor invokes {@link #initialize}. */ @SuppressWarnings("this-escape") public DefaultEncodingRegistry() { this.initialize(); // System.out.println("ENCODINGS BY ID:"); // new java.util.TreeMap<>(this.byId).forEach((k, v) -> System.out.println(String.format(" %-40s -> %s", k, v))); // System.out.println("ENCODINGS BY TYPETOKEN:"); // this.byType.forEach((k, v) -> System.out.println(String.format("%50s -> %s", k, v))); // System.out.println("CUSTOM REGISTRIES:"); // this.customEncodingRegistries.forEach(r -> System.out.println(" " + r)); } // EncodingRegistry @Override public EncodingId idForAlias(String alias) { return EncodingIds.idForAlias(alias); } @Override public String aliasForId(EncodingId encodingId) { return EncodingIds.aliasForId(encodingId); } @Override public synchronized Encoding getEncoding(EncodingId encodingId) { // See if we have it Encoding encoding = super.getEncoding(encodingId); if (encoding != null) return encoding; // Try custom registries encoding = this.customEncodingRegistries.stream() .map(registry -> registry.getEncoding(encodingId)) .filter(Objects::nonNull) .findFirst() .orElse(null); if (encoding != null) this.register(encodingId, encoding); // Done return encoding; } @Override public synchronized List> getEncodings(TypeToken typeToken) { // See if we have it List> encodingList = super.getEncodings(typeToken); if (!encodingList.isEmpty()) return encodingList; // Try custom registries return this.customEncodingRegistries.stream() .map(registry -> registry.getEncodings(typeToken)) .flatMap(List::stream) .peek(encoding -> this.register(encoding.getEncodingId(), encoding)) .collect(Collectors.toList()); } // Internal Methods /** * Initialize this instance. * *

* This method is invoked by the default constructor. The implementation in {@link DefaultEncodingRegistry} * invokes {@link #addBuiltinEncodings} and then {@link #findCustomEncodingRegistries}. */ protected void initialize() { this.addBuiltinEncodings(); this.findCustomEncodingRegistries(); } /** * Register Permazen's built-in encodings. * *

* The implementation in {@link DefaultEncodingRegistry} invokes {@link #addStandardBuiltinEncodings} * and then {@link #addOptionalBuiltinEncodings}. */ protected void addBuiltinEncodings() { this.addStandardBuiltinEncodings(); this.addOptionalBuiltinEncodings(); } /** * Register Permazen's standard built-in encodings. */ protected void addStandardBuiltinEncodings() { // Get primitive type EncodingId's final EncodingId z = EncodingIds.builtin("boolean"); final EncodingId b = EncodingIds.builtin("byte"); final EncodingId c = EncodingIds.builtin("char"); final EncodingId d = EncodingIds.builtin("double"); final EncodingId f = EncodingIds.builtin("float"); final EncodingId i = EncodingIds.builtin("int"); final EncodingId j = EncodingIds.builtin("long"); final EncodingId s = EncodingIds.builtin("short"); // Add primitive types this.add(new BooleanEncoding(z)); this.add(new ByteEncoding(b)); this.add(new CharacterEncoding(c)); this.add(new DoubleEncoding(d)); this.add(new FloatEncoding(f)); this.add(new IntegerEncoding(i)); this.add(new LongEncoding(j)); this.add(new ShortEncoding(s)); // Add primitive wrapper types this.add(new PrimitiveWrapperEncoding<>(new BooleanEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new ByteEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new CharacterEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new DoubleEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new FloatEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new IntegerEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new LongEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new ShortEncoding(null))); this.add(new PrimitiveWrapperEncoding<>(new VoidEncoding())); // Add primitive array types this.addNullSafe(z.getArrayId(), new BooleanArrayEncoding()); this.addNullSafe(b.getArrayId(), new ByteArrayEncoding()); this.addNullSafe(c.getArrayId(), new CharacterArrayEncoding()); this.addNullSafe(d.getArrayId(), new DoubleArrayEncoding()); this.addNullSafe(f.getArrayId(), new FloatArrayEncoding()); this.addNullSafe(i.getArrayId(), new IntegerArrayEncoding()); this.addNullSafe(j.getArrayId(), new LongArrayEncoding()); this.addNullSafe(s.getArrayId(), new ShortArrayEncoding()); // Built-in types in java.lang this.addWrappedBuiltin(new StringEncoding()); // Built-in types in java.math this.addWrappedBuiltin(new BigDecimalEncoding()); this.addWrappedBuiltin(new BigIntegerEncoding()); // Built-in types in java.io this.addBuiltin(File.class, FileEncoding::new); // Built-in types in java.util this.addWrappedBuiltin(new BitSetEncoding()); this.addWrappedBuiltin(new DateEncoding()); this.addWrappedBuiltin(new UUIDEncoding()); this.addWrappedBuiltin(new IntSummaryStatisticsEncoding()); this.addWrappedBuiltin(new LongSummaryStatisticsEncoding()); this.addWrappedBuiltin(new DoubleSummaryStatisticsEncoding()); // Built-in types in java.util.regex this.addBuiltin(Pattern.class, PatternEncoding::new); // Built-in types in java.net this.addWrappedBuiltin(new Inet4AddressEncoding()); this.addWrappedBuiltin(new Inet6AddressEncoding()); this.addWrappedBuiltin(new InetAddressEncoding()); this.addBuiltin(URI.class, URIEncoding::new); // Built-in types in java.time this.addWrappedBuiltin(new DurationEncoding()); this.addWrappedBuiltin(new InstantEncoding()); this.addWrappedBuiltin(new LocalDateTimeEncoding()); this.addWrappedBuiltin(new LocalDateEncoding()); this.addWrappedBuiltin(new LocalTimeEncoding()); this.addWrappedBuiltin(new MonthDayEncoding()); this.addWrappedBuiltin(new OffsetDateTimeEncoding()); this.addWrappedBuiltin(new OffsetTimeEncoding()); this.addWrappedBuiltin(new PeriodEncoding()); this.addWrappedBuiltin(new YearMonthEncoding()); this.addWrappedBuiltin(new YearEncoding()); this.addWrappedBuiltin(new ZoneOffsetEncoding()); this.addWrappedBuiltin(new ZonedDateTimeEncoding()); this.addBuiltin(ZoneId.class, ZoneIdEncoding::new); } private void addBuiltin(Class type, Function> ctor) { final Encoding encoding = ctor.apply(EncodingIds.builtin(type.getSimpleName())); Preconditions.checkArgument(encoding.supportsNull(), "encoding does not support null"); this.add(encoding); } private void addWrappedBuiltin(Encoding encoding) { final Class javaType = encoding.getTypeToken().getRawType(); final EncodingId encodingId = EncodingIds.builtin(javaType.getSimpleName()); this.addNullSafe(encodingId, encoding); } /** * Register Permazen's optional built-in encodings. * *

* The optional built-in encodings are ones that depend on optional dependencies. An example is * {@link InternetAddressEncoding} which depends on the Jakarta Mail API. */ protected void addOptionalBuiltinEncodings() { this.addOptionalBuiltinEncoding("InternetAddress", InternetAddressEncoding::new); } /** * Register a built-in encoding, but only if its target class is found on the classpath. * *

* The implementation in {@link DefaultEncodingRegistry} invokes the given {@code builder} but will catch * and ignore any {@link NoClassDefFoundError} thrown. Otherwise, the encoding is registered. * *

* No attempt to initialize * the encoding class should have occurred prior to invoking this method. * * @param name builtin encoding ID suffix * @param builder builder for encoding */ protected void addOptionalBuiltinEncoding(String name, Function> builder) { Preconditions.checkArgument(name != null, "null name"); Preconditions.checkArgument(builder != null, "null builder"); final EncodingId encodingId = EncodingIds.builtin(name); final Encoding encoding; try { encoding = builder.apply(encodingId); } catch (NoClassDefFoundError e) { this.log.debug("{}: not adding optional encoding \"{}\": {}", this.getClass().getSimpleName(), encodingId, e.toString()); return; } Preconditions.checkArgument(encoding != null, "null encoding returned from builder"); this.add(encoding); } /** * Scan the class path (via {@link ServiceLoader} and the current thread's context class loader) * for custom {@link EncodingRegistry} implementations */ protected void findCustomEncodingRegistries() { for (Iterator i = ServiceLoader.load(EncodingRegistry.class).iterator(); i.hasNext(); ) { final EncodingRegistry customEncodingRegistry = i.next(); this.log.debug("{}: including custom encoding registry {}", this.getClass().getSimpleName(), customEncodingRegistry); this.customEncodingRegistries.add(customEncodingRegistry); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy