/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.lucene.spatial3d.geom;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;
import java.util.List;

Indicates that a geo3d object can be serialized and deserialized.
@lucene.experimental
/** * Indicates that a geo3d object can be serialized and deserialized. * * @lucene.experimental */
public interface SerializableObject {
Serialize to output stream.
Params:
  • outputStream – is the output stream to write to.
/** Serialize to output stream. * @param outputStream is the output stream to write to. */
void write(OutputStream outputStream) throws IOException;
Write a PlanetObject to a stream.
Params:
  • outputStream – is the output stream.
  • object – is the object to write.
/** Write a PlanetObject to a stream. * @param outputStream is the output stream. * @param object is the object to write. */
public static void writePlanetObject(final OutputStream outputStream, final PlanetObject object) throws IOException { object.getPlanetModel().write(outputStream); writeObject(outputStream, object); }
Read a PlanetObject from a stream.
Params:
  • inputStream – is the input stream.
Returns:the PlanetObject.
/** Read a PlanetObject from a stream. * @param inputStream is the input stream. * @return the PlanetObject. */
public static PlanetObject readPlanetObject(final InputStream inputStream) throws IOException { final PlanetModel pm = new PlanetModel(inputStream); final SerializableObject so = readObject(pm, inputStream); if (!(so instanceof PlanetObject)) { throw new IOException("Type of object is not expected PlanetObject: "+so.getClass().getName()); } return (PlanetObject)so; }
Write an object to a stream.
Params:
  • outputStream – is the output stream.
  • object – is the object to write.
/** Write an object to a stream. * @param outputStream is the output stream. * @param object is the object to write. */
public static void writeObject(final OutputStream outputStream, final SerializableObject object) throws IOException { writeClass(outputStream, object.getClass()); object.write(outputStream); }
Read an object from a stream (for objects that need a PlanetModel).
Params:
  • planetModel – is the planet model to use to deserialize the object.
  • inputStream – is the input stream.
Returns:the deserialized object.
/** Read an object from a stream (for objects that need a PlanetModel). * @param planetModel is the planet model to use to deserialize the object. * @param inputStream is the input stream. * @return the deserialized object. */
public static SerializableObject readObject(final PlanetModel planetModel, final InputStream inputStream) throws IOException { try { // Read the class final Class<?> clazz = readClass(inputStream); return readObject(planetModel, inputStream, clazz); } catch (ClassNotFoundException e) { throw new IOException("Can't find class for deserialization: "+e.getMessage(), e); } }
Read an object from a stream (for objects that do not need a PlanetModel).
Params:
  • inputStream – is the input stream.
Returns:the deserialized object.
/** Read an object from a stream (for objects that do not need a PlanetModel). * @param inputStream is the input stream. * @return the deserialized object. */
public static SerializableObject readObject(final InputStream inputStream) throws IOException { try { // read the class final Class<?> clazz = readClass(inputStream); return readObject(inputStream, clazz); } catch (ClassNotFoundException e) { throw new IOException("Can't find class for deserialization: "+e.getMessage(), e); } }
Instantiate a serializable object from a stream.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
  • clazz – is the class to instantiate.
/** Instantiate a serializable object from a stream. * @param planetModel is the planet model. * @param inputStream is the input stream. * @param clazz is the class to instantiate. */
static SerializableObject readObject(final PlanetModel planetModel, final InputStream inputStream, final Class<?> clazz) throws IOException { try { // Look for the right constructor final Constructor<?> c = clazz.getDeclaredConstructor(PlanetModel.class, InputStream.class); // Invoke it final Object object = c.newInstance(planetModel, inputStream); // check whether caste will work if (!(object instanceof SerializableObject)) { throw new IOException("Object "+clazz.getName()+" does not implement SerializableObject"); } return (SerializableObject)object; } catch (InstantiationException e) { throw new IOException("Instantiation exception for class "+clazz.getName()+": "+e.getMessage(), e); } catch (IllegalAccessException e) { throw new IOException("Illegal access creating class "+clazz.getName()+": "+e.getMessage(), e); } catch (NoSuchMethodException e) { throw new IOException("No such method exception for class "+clazz.getName()+": "+e.getMessage(), e); } catch (InvocationTargetException e) { throw new IOException("Exception instantiating class "+clazz.getName()+": "+e.getMessage(), e); } }
Instantiate a serializable object from a stream without a planet model.
Params:
  • inputStream – is the input stream.
  • clazz – is the class to instantiate.
/** Instantiate a serializable object from a stream without a planet model. * @param inputStream is the input stream. * @param clazz is the class to instantiate. */
static SerializableObject readObject(final InputStream inputStream, final Class<?> clazz) throws IOException { try { // Look for the right constructor final Constructor<?> c = clazz.getDeclaredConstructor(InputStream.class); // Invoke it final Object object = c.newInstance(inputStream); // check whether caste will work if (!(object instanceof SerializableObject)) { throw new IOException("Object "+clazz.getName()+" does not implement SerializableObject"); } return (SerializableObject)object; } catch (InstantiationException e) { throw new IOException("Instantiation exception for class "+clazz.getName()+": "+e.getMessage(), e); } catch (IllegalAccessException e) { throw new IOException("Illegal access creating class "+clazz.getName()+": "+e.getMessage(), e); } catch (NoSuchMethodException e) { throw new IOException("No such method exception for class "+clazz.getName()+": "+e.getMessage(), e); } catch (InvocationTargetException e) { throw new IOException("Exception instantiating class "+clazz.getName()+": "+e.getMessage(), e); } }
Write a class to a stream.
Params:
  • outputStream – is the output stream.
  • clazz – is the class to write.
/** Write a class to a stream. * @param outputStream is the output stream. * @param clazz is the class to write. */
static void writeClass(final OutputStream outputStream, final Class<?> clazz) throws IOException { Integer index = StandardObjects.classRegsitry.get(clazz); if (index == null){ writeBoolean(outputStream, false); writeString(outputStream, clazz.getName()); } else { writeBoolean(outputStream, true); outputStream.write(index); } }
Read the class from the stream
Params:
  • inputStream – is the stream to read from.
Returns:is the class read
/** * Read the class from the stream * @param inputStream is the stream to read from. * @return is the class read */
static Class<?> readClass(final InputStream inputStream) throws IOException, ClassNotFoundException { boolean standard = readBoolean(inputStream); if (standard) { int index = inputStream.read(); return StandardObjects.codeRegsitry.get(index); } else { String className = readString(inputStream); return Class.forName(className); } }
Write a string to a stream.
Params:
  • outputStream – is the output stream.
  • value – is the string to write.
/** Write a string to a stream. * @param outputStream is the output stream. * @param value is the string to write. */
static void writeString(final OutputStream outputStream, final String value) throws IOException { writeByteArray(outputStream, value.getBytes(StandardCharsets.UTF_8)); }
Read a string from a stream.
Params:
  • inputStream – is the stream to read from.
Returns:the string that was read.
/** Read a string from a stream. * @param inputStream is the stream to read from. * @return the string that was read. */
static String readString(final InputStream inputStream) throws IOException { return new String(readByteArray(inputStream), StandardCharsets.UTF_8); }
Write a point array.
Params:
  • outputStream – is the output stream.
  • values – is the array of points to write.
/** Write a point array. * @param outputStream is the output stream. * @param values is the array of points to write. */
static void writePointArray(final OutputStream outputStream, final GeoPoint[] values) throws IOException { writeHomogeneousArray(outputStream, values); }
Write a point array.
Params:
  • outputStream – is the output stream.
  • values – is the list of points to write.
/** Write a point array. * @param outputStream is the output stream. * @param values is the list of points to write. */
static void writePointArray(final OutputStream outputStream, final List<GeoPoint> values) throws IOException { writeHomogeneousArray(outputStream, values); }
Read a point array.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
Returns:the array of points that was read.
/** Read a point array. * @param planetModel is the planet model. * @param inputStream is the input stream. * @return the array of points that was read. */
static GeoPoint[] readPointArray(final PlanetModel planetModel, final InputStream inputStream) throws IOException { return readHomogeneousArray(planetModel, inputStream, GeoPoint.class); }
Write a polgon array.
Params:
  • outputStream – is the output stream.
  • values – is the array of points to write.
/** Write a polgon array. * @param outputStream is the output stream. * @param values is the array of points to write. */
static void writePolygonArray(final OutputStream outputStream, final GeoPolygon[] values) throws IOException { writeHeterogeneousArray(outputStream, values); }
Write a polygon array.
Params:
  • outputStream – is the output stream.
  • values – is the list of points to write.
/** Write a polygon array. * @param outputStream is the output stream. * @param values is the list of points to write. */
static void writePolygonArray(final OutputStream outputStream, final List<GeoPolygon> values) throws IOException { writeHeterogeneousArray(outputStream, values); }
Read a polygon array.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
Returns:the array of polygons that was read.
/** Read a polygon array. * @param planetModel is the planet model. * @param inputStream is the input stream. * @return the array of polygons that was read. */
static GeoPolygon[] readPolygonArray(final PlanetModel planetModel, final InputStream inputStream) throws IOException { return readHeterogeneousArray(planetModel, inputStream, GeoPolygon.class); }
Write an array.
Params:
  • outputStream – is the output stream,.
  • values – is the array.
/** Write an array. * @param outputStream is the output stream,. * @param values is the array. */
static void writeHomogeneousArray(final OutputStream outputStream, final SerializableObject[] values) throws IOException { if (values == null) { writeInt(outputStream, 0); } else { writeInt(outputStream, values.length); for (final SerializableObject value : values) { value.write(outputStream); } } }
Write an array.
Params:
  • outputStream – is the output stream,.
  • values – is the array.
/** Write an array. * @param outputStream is the output stream,. * @param values is the array. */
static void writeHomogeneousArray(final OutputStream outputStream, final List<? extends SerializableObject> values) throws IOException { if (values == null) { writeInt(outputStream, 0); } else { writeInt(outputStream, values.size()); for (final SerializableObject value : values) { value.write(outputStream); } } }
Read an array.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
  • clazz – is the class of the objects to read.
Returns:the array.
/** Read an array. * @param planetModel is the planet model. * @param inputStream is the input stream. * @param clazz is the class of the objects to read. * @return the array. */
static <T extends SerializableObject> T[] readHomogeneousArray(final PlanetModel planetModel, final InputStream inputStream, final Class<T> clazz) throws IOException { final int count = readInt(inputStream); @SuppressWarnings("unchecked") final T[] rval = (T[])Array.newInstance(clazz, count); for (int i = 0; i < count; i++) { rval[i] = clazz.cast(readObject(planetModel, inputStream, clazz)); } return rval; }
Write an array.
Params:
  • outputStream – is the output stream,.
  • values – is the array.
/** Write an array. * @param outputStream is the output stream,. * @param values is the array. */
static void writeHeterogeneousArray(final OutputStream outputStream, final SerializableObject[] values) throws IOException { if (values == null) { writeInt(outputStream, 0); } else { writeInt(outputStream, values.length); for (final SerializableObject value : values) { writeObject(outputStream, value); } } }
Write an array.
Params:
  • outputStream – is the output stream,.
  • values – is the array.
/** Write an array. * @param outputStream is the output stream,. * @param values is the array. */
static void writeHeterogeneousArray(final OutputStream outputStream, final List<? extends SerializableObject> values) throws IOException { if (values == null) { writeInt(outputStream, 0); } else { writeInt(outputStream, values.size()); for (final SerializableObject value : values) { writeObject(outputStream, value); } } }
Read an array.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
Returns:the array.
/** Read an array. * @param planetModel is the planet model. * @param inputStream is the input stream. * @return the array. */
static <T extends SerializableObject> T[] readHeterogeneousArray(final PlanetModel planetModel, final InputStream inputStream, final Class<T> clazz) throws IOException { final int count = readInt(inputStream); @SuppressWarnings("unchecked") final T[] rval = (T[])Array.newInstance(clazz, count); for (int i = 0; i < count; i++) { rval[i] = clazz.cast(readObject(planetModel, inputStream)); } return rval; }
Write a bitset to a stream.
Params:
  • outputStream – is the output stream.
  • bitSet – is the bit set to write.
/** Write a bitset to a stream. * @param outputStream is the output stream. * @param bitSet is the bit set to write. */
static void writeBitSet(final OutputStream outputStream, final BitSet bitSet) throws IOException { writeByteArray(outputStream, bitSet.toByteArray()); }
Read a bitset from a stream.
Params:
  • inputStream – is the input stream.
Returns:the bitset read from the stream.
/** Read a bitset from a stream. * @param inputStream is the input stream. * @return the bitset read from the stream. */
static BitSet readBitSet(final InputStream inputStream) throws IOException { return BitSet.valueOf(readByteArray(inputStream)); }
Write byte array.
Params:
  • outputStream – is the output stream.
  • bytes – is the byte array.
/** Write byte array. * @param outputStream is the output stream. * @param bytes is the byte array. */
static void writeByteArray(final OutputStream outputStream, final byte[] bytes) throws IOException { writeInt(outputStream, bytes.length); outputStream.write(bytes); }
Read byte array.
Params:
  • inputStream – is the input stream.
Returns:the byte array.
/** Read byte array. * @param inputStream is the input stream. * @return the byte array. */
static byte[] readByteArray(final InputStream inputStream) throws IOException { int stringLength = readInt(inputStream); int stringOffset = 0; final byte[] bytes = new byte[stringLength]; while (stringLength > 0) { final int amt = inputStream.read(bytes, stringOffset, stringLength); if (amt == -1) { throw new IOException("Unexpected end of input stream"); } stringOffset += amt; stringLength -= amt; } return bytes; }
Write a double to a stream.
Params:
  • outputStream – is the output stream.
  • value – is the value to write.
/** Write a double to a stream. * @param outputStream is the output stream. * @param value is the value to write. */
static void writeDouble(final OutputStream outputStream, final double value) throws IOException { writeLong(outputStream, Double.doubleToLongBits(value)); }
Read a double from a stream.
Params:
  • inputStream – is the input stream.
Returns:the double value read from the stream.
/** Read a double from a stream. * @param inputStream is the input stream. * @return the double value read from the stream. */
static double readDouble(final InputStream inputStream) throws IOException { return Double.longBitsToDouble(readLong(inputStream)); }
Write a long to a stream.
Params:
  • outputStream – is the output stream.
  • value – is the value to write.
/** Write a long to a stream. * @param outputStream is the output stream. * @param value is the value to write. */
static void writeLong(final OutputStream outputStream, final long value) throws IOException { writeInt(outputStream, (int)value); writeInt(outputStream, (int)(value >> 32)); }
Read a long from a stream.
Params:
  • inputStream – is the input stream.
Returns:the long value read from the stream.
/** Read a long from a stream. * @param inputStream is the input stream. * @return the long value read from the stream. */
static long readLong(final InputStream inputStream) throws IOException { final long lower = ((long)(readInt(inputStream))) & 0x00000000ffffffffL; final long upper = (((long)(readInt(inputStream))) << 32) & 0xffffffff00000000L; return lower + upper; }
Write an int to a stream.
Params:
  • outputStream – is the output stream.
  • value – is the value to write.
/** Write an int to a stream. * @param outputStream is the output stream. * @param value is the value to write. */
static void writeInt(final OutputStream outputStream, final int value) throws IOException { outputStream.write(value); outputStream.write(value >> 8); outputStream.write(value >> 16); outputStream.write(value >> 24); }
Read an int from a stream.
Params:
  • inputStream – is the input stream.
Returns:the value read from the stream.
/** Read an int from a stream. * @param inputStream is the input stream. * @return the value read from the stream. */
static int readInt(final InputStream inputStream) throws IOException { final int l1 = (inputStream.read()) & 0x000000ff; final int l2 = (inputStream.read() << 8) & 0x0000ff00; final int l3 = (inputStream.read() << 16) & 0x00ff0000; final int l4 = (inputStream.read() << 24) & 0xff000000; return l1 + l2 + l3 + l4; }
Write a boolean to a stream.
Params:
  • outputStream – is the output stream.
  • value – is the value to write.
/** Write a boolean to a stream. * @param outputStream is the output stream. * @param value is the value to write. */
static void writeBoolean(final OutputStream outputStream, final boolean value) throws IOException { outputStream.write(value?1:0); }
Read a boolean from a stream.
Params:
  • inputStream – is the input stream.
Returns:the boolean value.
/** Read a boolean from a stream. * @param inputStream is the input stream. * @return the boolean value. */
static boolean readBoolean(final InputStream inputStream) throws IOException { final int valueRead = inputStream.read(); if (valueRead == -1) { throw new IOException("Unexpected end of input stream"); } return (valueRead == 0)?false:true; } }