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

io.jenetics.jpx.IO Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
/*
 * Java GPX Library (jpx-3.2.0).
 * Copyright (c) 2016-2024 Franz Wilhelmstötter
 *
 * 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.
 *
 * Author:
 *    Franz Wilhelmstötter ([email protected])
 */
package io.jenetics.jpx;

import static java.nio.charset.StandardCharsets.UTF_8;
import static io.jenetics.jpx.Lists.copyOf;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * Helper methods needed for implementing the Java serializations.
 *
 * @author Franz Wilhelmstötter
 * @version 1.2
 * @since 1.2
 */
final class IO {

	private IO() {
	}

	/**
	 * Object writer interface.
	 *
	 * @param  the object type
	 */
	@FunctionalInterface
	interface Writer {
		void write(final T value, final DataOutput out) throws IOException;
	}

	/**
	 * Object reader interface
	 *
	 * @param  the object type
	 */
	@FunctionalInterface
	interface Reader {
		T read(final DataInput in) throws IOException;
	}

	/**
	 * Write the given, possible {@code null}, {@code value} to the data output
	 * using the given {@code writer}.
	 *
	 * @param value the, possible {@code null}, value to write
	 * @param writer the object writer
	 * @param out the data output
	 * @param  the object type
	 * @throws NullPointerException if the {@code writer} or data output is
	 *         {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static  void writeNullable(
		final T value,
		final Writer writer,
		final DataOutput out
	)
		throws IOException
	{
		out.writeBoolean(value != null);
		if (value != null) {
			writer.write(value, out);
		}
	}

	/**
	 * Reads a possible {@code null} value from the given data input.
	 *
	 * @param reader the object reader
	 * @param in the data input
	 * @param  the object type
	 * @return the read object
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static  T readNullable(
		final Reader reader,
		final DataInput in
	)
		throws IOException
	{
		T value = null;
		if (in.readBoolean()) {
			value = reader.read(in);
		}

		return value;
	}

	/**
	 * Write the given string {@code value} to the given data output.
	 *
	 * @param value the string value to write
	 * @param out the data output
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static void writeString(final String value, final DataOutput out)
		throws IOException
	{
		final byte[] bytes = value.getBytes(UTF_8);
		writeInt(bytes.length, out);
		out.write(bytes);
	}

	/**
	 * Reads a string value from the given data input.
	 *
	 * @param in the data input
	 * @return the read string value
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static String readString(final DataInput in) throws IOException {
		final byte[] bytes = new byte[readInt(in)];
		in.readFully(bytes);
		return new String(bytes, UTF_8);
	}

	/**
	 * Write the given string, possible {@code null}, {@code value} to the given
	 * data output.
	 *
	 * @param value the string value to write
	 * @param out the data output
	 * @throws NullPointerException if the given data output is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static void writeNullableString(final String value, final DataOutput out)
		throws IOException
	{
		writeNullable(value, IO::writeString, out);
	}

	/**
	 * Reads a, possible {@code null}, string value from the given data input.
	 *
	 * @param in the data input
	 * @return the read string value
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static String readNullableString(final DataInput in) throws IOException {
		return readNullable(IO::readString, in);
	}

	/**
	 * Write the given elements to the data output.
	 *
	 * @param elements the elements to write
	 * @param writer the element writer
	 * @param out the data output
	 * @param  the element type
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static  void writes(
		final Collection elements,
		final Writer writer,
		final DataOutput out
	)
		throws IOException
	{
		writeInt(elements.size(), out);
		for (T element : elements) {
			writer.write(element, out);
		}
	}

	/**
	 * Reads a list of elements from the given data input.
	 *
	 * @param reader the element reader
	 * @param in the data input
	 * @param  the element type
	 * @return the read element list
	 * @throws NullPointerException if one of the given arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static  List reads(
		final Reader reader,
		final DataInput in
	)
		throws IOException
	{
		final int length = readInt(in);
		final List elements = new ArrayList<>(length);
		for (int i = 0; i < length; ++i) {
			elements.add(reader.read(in));
		}
		return copyOf(elements);
	}

	/**
	 * Writes an int value to a series of bytes. The values are written using
	 * variable-length
	 * zig-zag
	 * coding. Each {@code int} value is written in 1 to 5 bytes.
	 *
	 * @see #readInt(DataInput)
	 *
	 * @param value the integer value to write
	 * @param out the data output the integer value is written to
	 * @throws NullPointerException if the given data output is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static void writeInt(final int value, final DataOutput out) throws IOException {
		// Zig-zag encoding.
		int n = (value << 1)^(value >> 31);
		if ((n & ~0x7F) != 0) {
			out.write((byte)((n | 0x80) & 0xFF));
			n >>>= 7;
			if (n > 0x7F) {
				out.write((byte)((n | 0x80) & 0xFF));
				n >>>= 7;
				if (n > 0x7F) {
					out.write((byte)((n | 0x80) & 0xFF));
					n >>>= 7;
					if (n > 0x7F) {
						out.write((byte)((n | 0x80) & 0xFF));
						n >>>= 7;
					}
				}
			}
		}
		out.write((byte)n);
	}

	/**
	 * Reads an int value from the given data input. The integer value must have
	 * been written by the {@link #writeInt(int, DataOutput)} before.
	 *
	 * @see #writeInt(int, DataOutput)
	 *
	 * @param in the data input where the integer value is read from
	 * @return the read integer value
	 * @throws NullPointerException if the given data input is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static int readInt(final DataInput in) throws IOException {
		int b = in.readByte() & 0xFF;
		int n = b & 0x7F;

		if (b > 0x7F) {
			b = in.readByte() & 0xFF;
			n ^= (b & 0x7F) << 7;
			if (b > 0x7F) {
				b = in.readByte() & 0xFF;
				n ^= (b & 0x7F) << 14;
				if (b > 0x7F) {
					b = in.readByte() & 0xFF;
					n ^= (b & 0x7F) << 21;
					if (b > 0x7F) {
						b = in.readByte() & 0xFF;
						n ^= (b & 0x7F) << 28;
						if (b > 0x7F) {
							throw new IOException("Invalid int encoding.");
						}
					}
				}
			}
		}

		return (n >>> 1)^-(n & 1);
	}

	/**
	 * Writes a long value to a series of bytes. The values are written using
	 * variable-length
	 * zig-zag
	 * coding. Each {@code long} value is written in 1 to 10 bytes.
	 *
	 * @see #readLong(DataInput)
	 *
	 * @param value the long value to write
	 * @param out the data output the integer value is written to
	 * @throws NullPointerException if the given data output is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static void writeLong(final long value, final DataOutput out)
		throws IOException
	{
		// Zig-zag encoding.
		long n = (value << 1)^(value >> 63);
		if ((n & ~0x7FL) != 0) {
			out.write((byte)((n | 0x80) & 0xFF));
			n >>>= 7;
			if (n > 0x7F) {
				out.write((byte)((n | 0x80) & 0xFF));
				n >>>= 7;
				if (n > 0x7F) {
					out.write((byte)((n | 0x80) & 0xFF));
					n >>>= 7;
					if (n > 0x7F) {
						out.write((byte)((n | 0x80) & 0xFF));
						n >>>= 7;
						if (n > 0x7F) {
							out.write((byte)((n | 0x80) & 0xFF));
							n >>>= 7;
							if (n > 0x7F) {
								out.write((byte)((n | 0x80) & 0xFF));
								n >>>= 7;
								if (n > 0x7F) {
									out.write((byte)((n | 0x80) & 0xFF));
									n >>>= 7;
									if (n > 0x7F) {
										out.write((byte)((n | 0x80) & 0xFF));
										n >>>= 7;
										if (n > 0x7F) {
											out.write((byte)((n | 0x80) & 0xFF));
											n >>>= 7;
										}
									}
								}
							}
						}
					}
				}
			}
		}
		out.write((byte)n);
	}

	/**
	 * Reads a long value from the given data input. The integer value must have
	 * been written by the {@link #writeInt(int, DataOutput)} before.
	 *
	 * @see #writeLong(long, DataOutput)
	 *
	 * @param in the data input where the integer value is read from
	 * @return the read long value
	 * @throws NullPointerException if the given data input is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static long readLong(final DataInput in) throws IOException {
		int b = in.readByte() & 0xFF;
		int n = b & 0x7F;
		long l;
		if (b > 0x7F) {
			b = in.readByte() & 0xFF;
			n ^= (b & 0x7F) << 7;
			if (b > 0x7F) {
				b = in.readByte() & 0xFF;
				n ^= (b & 0x7F) << 14;
				if (b > 0x7F) {
					b = in.readByte() & 0xFF;
					n ^= (b & 0x7F) << 21;
					if (b > 0x7F) {
						l = innerLongDecode(n, in);
					} else {
						l = n;
					}
				} else {
					l = n;
				}
			} else {
				l = n;
			}
		} else {
			l = n;
		}
		return (l >>> 1)^-(l & 1);
	}

	private static long innerLongDecode(long l, final DataInput in)
		throws IOException
	{
		int b = in.readByte() & 0xFF;
		l ^= (b & 0x7FL) << 28;
		if (b > 0x7F) {
			b = in.readByte() & 0xFF;
			l ^= (b & 0x7FL) << 35;
			if (b > 0x7F) {
				b = in.readByte() & 0xFF;
				l ^= (b & 0x7FL) << 42;
				if (b > 0x7F) {
					b = in.readByte() & 0xFF;
					l ^= (b & 0x7FL) << 49;
					if (b > 0x7F) {
						b = in.readByte() & 0xFF;
						l ^= (b & 0x7FL) << 56;
						if (b > 0x7F) {
							b = in.readByte() & 0xFF;
							l ^= (b & 0x7FL) << 63;
							if (b > 0x7F) {
								throw new IOException("Invalid long encoding.");
							}
						}
					}
				}
			}
		}
		return l;
	}

	/**
	 * Write the given XML document to the given data output.
	 *
	 * @since 1.5
	 *
	 * @param value the value to write
	 * @param out the data output the integer value is written to
	 * @throws NullPointerException if one of the arguments is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static void write(final Document value, final DataOutput out)
		throws IOException
	{
		final ByteArrayOutputStream bout = new ByteArrayOutputStream();
		XML.copy(value, bout);

		final byte[] data = bout.toByteArray();
		IO.writeInt(data.length, out);
		out.write(data);
	}

	/**
	 * Reads a long value from the given data input. The integer value must have
	 * been written by the {@link #writeInt(int, DataOutput)} before.
	 *
	 * @since 1.5
	 *
	 * @param in the data input where the value is read from
	 * @return the read XML document
	 * @throws NullPointerException if the given data input is {@code null}
	 * @throws IOException if an I/O error occurs
	 */
	static Document readDoc(final DataInput in) throws IOException {
		final int length = IO.readInt(in);
		final byte[] data = new byte[length];
		in.readFully(data);

		final ByteArrayInputStream bin = new ByteArrayInputStream(data);
		try {
			return XMLProvider
				.provider()
				.documentBuilderFactory()
				.newDocumentBuilder()
				.parse(bin);
		} catch (ParserConfigurationException|SAXException e) {
			throw new IOException(e);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy