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

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

There is a newer version: 3.1.0
Show newest version
/*
 * Copyright (C) 2015-2018 SoftIndex 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.datakernel.codec;

import io.datakernel.common.parse.ParseException;
import io.datakernel.common.tuple.*;

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

import static io.datakernel.common.Preconditions.checkArgument;
import static io.datakernel.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 ParseException {
			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 ParseException {
			String v = in.readString();
			if (v.length() == 1) {
				return v.charAt(0);
			}
			throw new ParseException("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 ParseException {
			int v = in.readInt();
			if (v >= 0 && v <= 0xFF) {
				return (byte) v;
			}
			throw new ParseException("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 ParseException {
			int v = in.readInt();
			if (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE) {
				return (short) v;
			}
			throw new ParseException("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 ParseException {
			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 ParseException {
			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 ParseException {
			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 ParseException {
			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 ParseException {
			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 ParseException {
			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 ParseException {
			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 ParseException {
			return in.readBytes();
		}
	};

	public static final StructuredCodec VOID_CODEC = new StructuredCodec() {
		@Override
		public Void decode(StructuredInput in) throws ParseException {
			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 ParseException {
				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 ParseException {
			try {
				return Class.forName(in.readString());
			} catch (ClassNotFoundException e) {
				throw new ParseException(e);
			}
		}
	};

	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 ParseException {
				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 Optional decode(StructuredInput in) throws ParseException {
				return Optional.ofNullable(in.readNullable(codec));
			}

			@Override
			public void encode(StructuredOutput out, Optional item) {
				out.writeNullable(codec, item.orElse(null));
			}
		};
	}

	/**
	 * 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 List decode(StructuredInput in) throws ParseException {
				return in.readList(valueAdapters);
			}

			@Override
			public void encode(StructuredOutput out, List item) {
				out.writeList(valueAdapters, item);
			}
		};
	}

	/**
	 * 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 ParseException {
				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 ParseException {
				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 ParseException {
				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 ParseException {
				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(TupleParser0 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(TupleParser1 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(TupleParser2 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(TupleParser3 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(TupleParser4 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(TupleParser5 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(TupleParser6 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(TupleParser0 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(TupleParser1 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(TupleParser2 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(TupleParser3 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(TupleParser4 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(TupleParser5 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(TupleParser6 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