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

io.activej.codec.StructuredCodecs Maven / Gradle / Ivy

There is a newer version: 4.4-rc1
Show newest version
/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * 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 io.activej.codec;

import io.activej.common.api.DecoderFunction;
import io.activej.common.exception.MalformedDataException;
import io.activej.common.exception.UncheckedException;
import io.activej.common.tuple.*;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;

import static io.activej.common.Checks.checkArgument;
import static io.activej.common.collection.CollectionUtils.map;
import static java.util.Arrays.asList;
import static java.util.Collections.*;

/**
 * This class contains various primitive {@link StructuredCodec StructuredCodecs} and their combinators.
 */
@SuppressWarnings("unchecked")
public final class StructuredCodecs {

	public static final StructuredCodec BOOLEAN_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Boolean value) {
			out.writeBoolean(value);
		}

		@Override
		public Boolean decode(StructuredInput in) throws MalformedDataException {
			return in.readBoolean();
		}
	};

	public static final StructuredCodec CHARACTER_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Character value) {
			out.writeString(value + "");
		}

		@Override
		public Character decode(StructuredInput in) throws MalformedDataException {
			String v = in.readString();
			if (v.length() == 1) {
				return v.charAt(0);
			}
			throw new MalformedDataException("Read a string with length != 1 while trying to read a character");
		}
	};

	public static final StructuredCodec BYTE_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Byte value) {
			out.writeInt(value & 0xFF);
		}

		@Override
		public Byte decode(StructuredInput in) throws MalformedDataException {
			int v = in.readInt();
			if (v >= 0 && v <= 0xFF) {
				return (byte) v;
			}
			throw new MalformedDataException("Read an int not in range [0, 255] while trying to read a byte");
		}
	};

	public static final StructuredCodec SHORT_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Short value) {
			out.writeInt(value);
		}

		@Override
		public Short decode(StructuredInput in) throws MalformedDataException {
			int v = in.readInt();
			if (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE) {
				return (short) v;
			}
			throw new MalformedDataException("Read an int not in range [" + Short.MIN_VALUE + ", " + Short.MAX_VALUE + "] while trying to read a short");
		}
	};

	public static final StructuredCodec INT_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Integer value) {
			out.writeInt(value);
		}

		@Override
		public Integer decode(StructuredInput in) throws MalformedDataException {
			return in.readInt();
		}
	};

	public static final StructuredCodec LONG_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Long value) {
			out.writeLong(value);
		}

		@Override
		public Long decode(StructuredInput in) throws MalformedDataException {
			return in.readLong();
		}
	};

	public static final StructuredCodec INT32_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Integer value) {
			out.writeInt32(value);
		}

		@Override
		public Integer decode(StructuredInput in) throws MalformedDataException {
			return in.readInt32();
		}
	};

	public static final StructuredCodec LONG64_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Long value) {
			out.writeLong64(value);
		}

		@Override
		public Long decode(StructuredInput in) throws MalformedDataException {
			return in.readLong64();
		}
	};

	public static final StructuredCodec FLOAT_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Float value) {
			out.writeFloat(value);
		}

		@Override
		public Float decode(StructuredInput in) throws MalformedDataException {
			return in.readFloat();
		}
	};

	public static final StructuredCodec DOUBLE_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, Double value) {
			out.writeDouble(value);
		}

		@Override
		public Double decode(StructuredInput in) throws MalformedDataException {
			return in.readDouble();
		}
	};

	public static final StructuredCodec STRING_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, String value) {
			out.writeString(value);
		}

		@Override
		public String decode(StructuredInput in) throws MalformedDataException {
			return in.readString();
		}
	};

	public static final StructuredCodec BYTES_CODEC = new StructuredCodec() {
		@Override
		public void encode(StructuredOutput out, byte[] value) {
			out.writeBytes(value);
		}

		@Override
		public byte[] decode(StructuredInput in) throws MalformedDataException {
			return in.readBytes();
		}
	};

	public static final StructuredCodec VOID_CODEC = new StructuredCodec() {
		@Override
		public Void decode(StructuredInput in) throws MalformedDataException {
			in.readNull();
			return null;
		}

		@Override
		public void encode(StructuredOutput out, Void item) {
			out.writeNull();
		}
	};

	public static > StructuredCodec ofEnum(Class enumType) {
		return new StructuredCodec() {
			@Override
			public void encode(StructuredOutput out, E value) {
				out.writeString(value.name());
			}

			@Override
			public E decode(StructuredInput in) throws MalformedDataException {
				return Enum.valueOf(enumType, in.readString());
			}
		};
	}

	public static final StructuredCodec> CLASS_CODEC = new StructuredCodec>() {
		@Override
		public void encode(StructuredOutput out, Class value) {
			out.writeString(value.getName());
		}

		@Override
		public Class decode(StructuredInput in) throws MalformedDataException {
			try {
				return Class.forName(in.readString());
			} catch (ClassNotFoundException e) {
				throw new MalformedDataException(e);
			}
		}
	};

	@SuppressWarnings("rawtypes")
	public static  StructuredCodec> ofClass() {
		return (StructuredCodec) CLASS_CODEC;
	}

	static  StructuredCodec ofCustomType(Class type) {
		return ofCustomType((Type) type);
	}

	static  StructuredCodec ofCustomType(Type type) {
		return new StructuredCodec() {
			@Override
			public void encode(StructuredOutput out, T item) {
				out.writeCustom(type, item);
			}

			@Override
			public T decode(StructuredInput in) throws MalformedDataException {
				return in.readCustom(type);
			}
		};
	}

	/**
	 * Combinator codec that writes/reads Optional<T> as nullable T with given codec for T
	 */
	public static  StructuredCodec> ofOptional(StructuredCodec codec) {
		return new StructuredCodec>() {
			@Override
			public void encode(StructuredOutput out, Optional item) {
				out.writeNullable(codec, item.orElse(null));
			}

			@Override
			public Optional decode(StructuredInput in) throws MalformedDataException {
				return Optional.ofNullable(in.readNullable(codec));
			}
		};
	}

	public static  StructuredCodec<@Nullable T> ofNullable(StructuredCodec codec) {
		return new StructuredCodec() {
			@Override
			public void encode(StructuredOutput out, T item) {
				out.writeNullable(codec, item);
			}

			@Nullable
			@Override
			public T decode(StructuredInput in) throws MalformedDataException {
				return in.readNullable(codec);
			}
		};
	}

	public static  StructuredCodec transform(StructuredCodec codec, DecoderFunction reader, Function writer) {
		return new StructuredCodec() {
			@Override
			public void encode(StructuredOutput out, R value) {
				T result = writer.apply(value);
				codec.encode(out, result);
			}

			@Override
			public R decode(StructuredInput in) throws MalformedDataException {
				T result = codec.decode(in);
				try {
					return reader.decode(result);
				} catch (UncheckedException u) {
					throw u.propagate(MalformedDataException.class);
				}
			}
		};
	}


	/**
	 * Combinator codec that writes/reads a list of T with given codec for T
	 */
	public static  StructuredCodec> ofList(StructuredCodec valueAdapters) {
		return new StructuredCodec>() {
			@Override
			public void encode(StructuredOutput out, List item) {
				out.writeList(valueAdapters, item);
			}

			@Override
			public List decode(StructuredInput in) throws MalformedDataException {
				return in.readList(valueAdapters);
			}
		};
	}

	/**
	 * Combinator codec that writes/reads a set of T with given codec for T
	 */
	public static  StructuredCodec> ofSet(StructuredCodec codec) {
		return ofList(codec)
				.transform(LinkedHashSet::new, ArrayList::new);
	}

	/**
	 * Combinator codec that writes/reads a heterogeneous fixed-size array ob objects with given codecs
	 */
	public static StructuredCodec ofTupleArray(StructuredCodec... elementDecoders) {
		return ofTupleList(asList(elementDecoders))
				.transform(
						list -> list.toArray(new Object[0]),
						Arrays::asList
				);
	}

	public static StructuredCodec ofTupleArray(List> codecs) {
		return ofTupleList(codecs)
				.transform(
						list -> list.toArray(new Object[0]),
						Arrays::asList
				);
	}

	public static  StructuredCodec> ofTupleList(StructuredCodec... elementDecoders) {
		return ofTupleList(asList(elementDecoders));
	}

	public static  StructuredCodec> ofTupleList(List> codecs) {
		return new StructuredCodec>() {
			@Override
			public List decode(StructuredInput in) throws MalformedDataException {
				return in.readTuple($ -> {
					List list = new ArrayList<>();
					for (StructuredCodec codec : codecs) {
						list.add(codec.decode(in));
					}
					return list;
				});
			}

			@Override
			public void encode(StructuredOutput out, List list) {
				checkArgument(list.size() == codecs.size());
				Iterator it = list.iterator();
				out.writeTuple(() -> {
					for (StructuredCodec codec : codecs) {
						((StructuredCodec) codec).encode(out, it.next());
					}
				});
			}
		};
	}

	/**
	 * Combinator codec that writes/reads a map with keys of type K and values of type V with given codecs for K and V
	 */
	public static  StructuredCodec> ofMap(StructuredCodec codecKey, StructuredCodec codecValue) {
		return new StructuredCodec>() {
			@Override
			public void encode(StructuredOutput out, Map map) {
				out.writeMap(codecKey, codecValue, map);
			}

			@Override
			public Map decode(StructuredInput in) throws MalformedDataException {
				return in.readMap(codecKey, codecValue);
			}
		};
	}

	/**
	 * Combinator codec that writes/reads a heterogeneous map with string keys and values using codecs from given map
	 */
	public static  StructuredCodec> ofObjectMap(Map> fieldCodecs) {
		return new StructuredCodec>() {
			@Override
			public Map decode(StructuredInput in) throws MalformedDataException {
				return in.readObject($ -> {
					Map map = new LinkedHashMap<>();
					for (Map.Entry> entry : fieldCodecs.entrySet()) {
						String field = entry.getKey();
						in.readKey(field);
						StructuredCodec codec = entry.getValue();
						map.put(field, codec.decode(in));
					}
					return map;
				});
			}

			@Override
			public void encode(StructuredOutput out, Map map) {
				out.writeObject(() -> {
					for (Map.Entry> entry : fieldCodecs.entrySet()) {
						String field = entry.getKey();
						out.writeKey(field);
						StructuredCodec codec = (StructuredCodec) entry.getValue();
						codec.encode(out, map.get(field));
					}
				});
			}
		};
	}

	public static  StructuredCodec> concat(StructuredCodec... elementCodecs) {
		return concat(asList(elementCodecs));
	}

	/**
	 * Combinator codec that writes/reads a heterogeneous list using codecs from given list without list boundaries
	 */
	public static  StructuredCodec> concat(List> elementCodecs) {
		return new StructuredCodec>() {
			@Override
			public List decode(StructuredInput in) throws MalformedDataException {
				List result = new ArrayList<>(elementCodecs.size());
				for (StructuredCodec elementCodec : elementCodecs) {
					result.add(elementCodec.decode(in));
				}
				return result;
			}

			@Override
			public void encode(StructuredOutput out, List item) {
				checkArgument(item.size() == elementCodecs.size());
				for (int i = 0; i < elementCodecs.size(); i++) {
					((StructuredCodec) elementCodecs.get(i)).encode(out, item.get(i));
				}
			}
		};
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder0 constructor) {
		return ofTupleList()
				.transform(
						list -> constructor.create(),
						item -> emptyList());
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder1 constructor,
			Function getter1, StructuredCodec codec1) {
		return ofTupleList(codec1)
				.transform(
						list -> constructor.create(list.get(0)),
						item -> singletonList(getter1.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder2 constructor,
			Function getter1, StructuredCodec codec1,
			Function getter2, StructuredCodec codec2) {
		return ofTupleList(codec1, codec2)
				.transform(
						list -> constructor.create((T1) list.get(0), (T2) list.get(1)),
						item -> asList(getter1.apply(item), getter2.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder3 constructor,
			Function getter1, StructuredCodec codec1,
			Function getter2, StructuredCodec codec2,
			Function getter3, StructuredCodec codec3) {
		return ofTupleList(codec1, codec2, codec3)
				.transform(
						list -> constructor.create((T1) list.get(0), (T2) list.get(1), (T3) list.get(2)),
						item -> asList(getter1.apply(item), getter2.apply(item), getter3.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder4 constructor,
			Function getter1, StructuredCodec codec1,
			Function getter2, StructuredCodec codec2,
			Function getter3, StructuredCodec codec3,
			Function getter4, StructuredCodec codec4) {
		return ofTupleList(codec1, codec2, codec3, codec4)
				.transform(
						list -> constructor.create((T1) list.get(0), (T2) list.get(1), (T3) list.get(2), (T4) list.get(3)),
						item -> asList(getter1.apply(item), getter2.apply(item), getter3.apply(item), getter4.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder5 constructor,
			Function getter1, StructuredCodec codec1,
			Function getter2, StructuredCodec codec2,
			Function getter3, StructuredCodec codec3,
			Function getter4, StructuredCodec codec4,
			Function getter5, StructuredCodec codec5) {
		return ofTupleList(codec1, codec2, codec3, codec4, codec5)
				.transform(
						list -> constructor.create((T1) list.get(0), (T2) list.get(1), (T3) list.get(2), (T4) list.get(3), (T5) list.get(4)),
						item -> asList(getter1.apply(item), getter2.apply(item), getter3.apply(item), getter4.apply(item), getter5.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofTupleList(List)} with fixed number of arguments and map it to some result type R
	 */
	public static  StructuredCodec tuple(TupleDecoder6 constructor,
			Function getter1, StructuredCodec codec1,
			Function getter2, StructuredCodec codec2,
			Function getter3, StructuredCodec codec3,
			Function getter4, StructuredCodec codec4,
			Function getter5, StructuredCodec codec5,
			Function getter6, StructuredCodec codec6) {
		return ofTupleList(codec1, codec2, codec3, codec4, codec5, codec6)
				.transform(
						list -> constructor.create((T1) list.get(0), (T2) list.get(1), (T3) list.get(2), (T4) list.get(3), (T5) list.get(4), (T6) list.get(5)),
						item -> asList(getter1.apply(item), getter2.apply(item), getter3.apply(item), getter4.apply(item), getter5.apply(item), getter6.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder0 constructor) {
		return ofObjectMap(emptyMap())
				.transform(
						map -> constructor.create(),
						item -> map());
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder1 constructor,
			String field1, Function getter1, StructuredCodec codec1) {
		return ofObjectMap(map(field1, codec1))
				.transform(
						map -> constructor.create(map.get(field1)),
						item -> map(field1, getter1.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder2 constructor,
			String field1, Function getter1, StructuredCodec codec1,
			String field2, Function getter2, StructuredCodec codec2) {
		return ofObjectMap(map(field1, codec1, field2, codec2))
				.transform(
						map -> constructor.create((T1) map.get(field1), (T2) map.get(field2)),
						item -> map(field1, getter1.apply(item), field2, getter2.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder3 constructor,
			String field1, Function getter1, StructuredCodec codec1,
			String field2, Function getter2, StructuredCodec codec2,
			String field3, Function getter3, StructuredCodec codec3) {
		return ofObjectMap(map(field1, codec1, field2, codec2, field3, codec3))
				.transform(
						map -> constructor.create((T1) map.get(field1), (T2) map.get(field2), (T3) map.get(field3)),
						item -> map(field1, getter1.apply(item), field2, getter2.apply(item), field3, getter3.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder4 constructor,
			String field1, Function getter1, StructuredCodec codec1,
			String field2, Function getter2, StructuredCodec codec2,
			String field3, Function getter3, StructuredCodec codec3,
			String field4, Function getter4, StructuredCodec codec4) {
		return ofObjectMap(map(field1, codec1, field2, codec2, field3, codec3, field4, codec4))
				.transform(
						map -> constructor.create((T1) map.get(field1), (T2) map.get(field2), (T3) map.get(field3), (T4) map.get(field4)),
						item -> map(field1, getter1.apply(item), field2, getter2.apply(item), field3, getter3.apply(item), field4, getter4.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder5 constructor,
			String field1, Function getter1, StructuredCodec codec1,
			String field2, Function getter2, StructuredCodec codec2,
			String field3, Function getter3, StructuredCodec codec3,
			String field4, Function getter4, StructuredCodec codec4,
			String field5, Function getter5, StructuredCodec codec5) {
		return ofObjectMap(map(field1, codec1, field2, codec2, field3, codec3, field4, codec4, field5, codec5))
				.transform(
						map -> constructor.create((T1) map.get(field1), (T2) map.get(field2), (T3) map.get(field3), (T4) map.get(field4), (T5) map.get(field5)),
						item -> map(field1, getter1.apply(item), field2, getter2.apply(item), field3, getter3.apply(item), field4, getter4.apply(item), field5, getter5.apply(item)));
	}

	/**
	 * A DSL to call {@link #ofObjectMap(Map)} with fixed number of key-value pairs and map it to some result type R.
	 * This is the main combinator for POJO codecs
	 */
	public static  StructuredCodec object(TupleDecoder6 constructor,
			String field1, Function getter1, StructuredCodec codec1,
			String field2, Function getter2, StructuredCodec codec2,
			String field3, Function getter3, StructuredCodec codec3,
			String field4, Function getter4, StructuredCodec codec4,
			String field5, Function getter5, StructuredCodec codec5,
			String field6, Function getter6, StructuredCodec codec6) {
		return ofObjectMap(map(field1, codec1, field2, codec2, field3, codec3, field4, codec4, field5, codec5, field6, codec6))
				.transform(
						map -> constructor.create((T1) map.get(field1), (T2) map.get(field2), (T3) map.get(field3), (T4) map.get(field4), (T5) map.get(field5), (T6) map.get(field6)),
						item -> map(field1, getter1.apply(item), field2, getter2.apply(item), field3, getter3.apply(item), field4, getter4.apply(item), field5, getter5.apply(item), field6, getter6.apply(item)));
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy