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

org.conqat.lib.commons.serialization.classes.SerializedClass Maven / Gradle / Ivy

There is a newer version: 2024.7.2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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 org.conqat.lib.commons.serialization.classes;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectStreamConstants;
import java.util.ArrayList;
import java.util.List;

import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableList;
import org.conqat.lib.commons.serialization.SerializedEntityParser;
import org.conqat.lib.commons.serialization.SerializedEntityPool;
import org.conqat.lib.commons.serialization.SerializedEntitySerializer;
import org.conqat.lib.commons.serialization.objects.SerializedObject;

/**
 * A serialized class.
 */
public class SerializedClass extends SerializedClassBase {

	/**
	 * The class description flags used for enums (found by experimentation).
	 */
	private static final byte ENUM_CLASS_DESCRIPTION_FLAGS = 18;

	private static final byte CLASS_DESCRIPTION_FLAGS = 2;

	/** Fully qualified class name. */
	private String name;

	/** The serial version uid. */
	private long serialVersionUid;

	/** The class description flags. */
	private byte classDescriptionFlags;

	/** The fields of the class. */
	private List fields;

	/** Constructor. */
	public SerializedClass(DataInputStream din, SerializedEntityPool pool, SerializedEntityParser parser)
			throws IOException {
		super(din, pool, parser);
	}

	/** Direct constructor. */
	public SerializedClass(String name, long serialVersionUid, byte classDescriptionFlags, SerializedEntityPool pool) {
		super(pool);
		this.name = name;
		this.serialVersionUid = serialVersionUid;
		this.classDescriptionFlags = classDescriptionFlags;
		this.fields = new ArrayList<>();
	}

	/** {@inheritDoc} */
	@Override
	protected void parseClass(DataInputStream din, SerializedEntityPool pool, SerializedEntityParser parser)
			throws IOException {
		this.name = din.readUTF();
		this.serialVersionUid = din.readLong();
		this.classDescriptionFlags = din.readByte();

		short fieldCount = din.readShort();
		fields = new ArrayList<>();
		for (int i = 0; i < fieldCount; ++i) {
			fields.add(readFieldDescription(din, parser));
		}
	}

	/** Reads the fieldDesc part of the stream. */
	private static SerializedFieldBase readFieldDescription(DataInputStream din, SerializedEntityParser parser)
			throws IOException {
		byte next = din.readByte();
		String fieldName = din.readUTF();

		switch (next) {
		case SerializedArrayField.TYPE_CODE:
			return new SerializedArrayField(fieldName, parser);
		case SerializedObjectField.TYPE_CODE:
			return new SerializedObjectField(fieldName, parser);
		default:
			return SerializedPrimitiveFieldBase.fromTypeCode((char) next, fieldName);
		}
	}

	/** {@inheritDoc} */
	@Override
	protected void serializeClass(DataOutputStream dos, SerializedEntitySerializer serializer) throws IOException {
		dos.writeByte(ObjectStreamConstants.TC_CLASSDESC);

		dos.writeUTF(name);
		dos.writeLong(serialVersionUid);
		dos.writeByte(classDescriptionFlags);

		dos.writeShort(fields.size());

		// during serialization, the primitive types are required to come first,
		// as otherwise we get an exception
		for (SerializedFieldBase field : fields) {
			if (field instanceof SerializedPrimitiveFieldBase) {
				field.serialize(dos, serializer);
			}
		}
		for (SerializedFieldBase field : fields) {
			if (!(field instanceof SerializedPrimitiveFieldBase)) {
				field.serialize(dos, serializer);
			}
		}
	}

	/** Returns the name. */
	public String getName() {
		return name;
	}

	/** Sets the name. */
	public void setName(String name) {
		this.name = name;
	}

	/** Returns the serial version. */
	public long getSerialVersionUid() {
		return serialVersionUid;
	}

	/** Sets the serial version uid. */
	public void setSerialVersionUid(long serialVersionUid) {
		this.serialVersionUid = serialVersionUid;
	}

	/** Returns the class description flags. */
	public byte getClassDescriptionFlags() {
		return classDescriptionFlags;
	}

	/** Returns the fields of this class. */
	public UnmodifiableList getFields() {
		return CollectionUtils.asUnmodifiable(fields);
	}

	/** {@inheritDoc} */
	@Override
	public String toString() {
		return "Plain class " + name;
	}

	/** Returns the field by name (or null if not found). */
	public SerializedFieldBase getField(String name) {
		// we do not use a map to speed up lookup as field names may be changed
		for (SerializedFieldBase field : fields) {
			if (field.getName().equals(name)) {
				return field;
			}
		}
		return null;
	}

	/** Returns whether this class has a field with the given name. */
	public boolean containsField(String name) {
		return getField(name) != null;
	}

	/**
	 * Adds a field to this class. Note that the instances of this class have to be adjusted
	 * accordingly, otherwise you will get exceptions during serialization.
	 */
	public void addField(SerializedFieldBase newField) {
		CCSMAssert.isFalse(containsField(newField.getName()),
				"Field with name '" + newField.getName() + "' already exists in " + name);
		fields.add(newField);
	}

	/**
	 * Removes the field of given name from this class. Note that the instances of this class have to be
	 * adjusted accordingly, otherwise you will get exceptions during serialization.
	 */
	public void removeField(String name) {
		fields.removeIf(field -> field.getName().equals(name));
	}

	/**
	 * Renames the field of the {@link SerializedClass} and adjusts the corresponding field values in
	 * all {@link SerializedObject}s of the {@link SerializedClass}.
	 */
	public void renameField(String oldFieldName, String newFieldName) {
		SerializedFieldBase field = getField(oldFieldName);

		// Can happen in case migrations occur across multiple file system versions with
		// one migration to add the field being missing.
		if (field != null) {
			field.setName(newFieldName);
		}
	}

	/** Returns whether this is an externalizable class. */
	public boolean isExternalizable() {
		return (classDescriptionFlags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
	}

	/** Returns whether this is a serializable class. */
	public boolean isSerializable() {
		return (classDescriptionFlags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
	}

	/**
	 * Returns whether this class has a custom write method (only relevant for serializable).
	 */
	public boolean hasWriteMethod() {
		return (classDescriptionFlags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
	}

	/**
	 * Returns whether this class stores block data (only relevant for externalizable).
	 */
	public boolean hasBlockData() {
		return (classDescriptionFlags & ObjectStreamConstants.SC_BLOCK_DATA) != 0;
	}

	/** Factory method for creating a new enum class. */
	public static SerializedClass createSimpleEnum(SerializedEntityPool entityPool, String enumName) {
		SerializedClass resultClass = new SerializedClass(enumName, 0, ENUM_CLASS_DESCRIPTION_FLAGS, entityPool);

		SerializedClass enumClass = entityPool.findClass(Enum.class.getName());
		if (enumClass == null) {
			enumClass = new SerializedClass(Enum.class.getName(), 0, ENUM_CLASS_DESCRIPTION_FLAGS, entityPool);
		}
		resultClass.superClassHandle = enumClass.getHandle();

		return resultClass;
	}

	/** Creates a class with the specified name in the given entity pool. */
	public static SerializedClass createClass(SerializedEntityPool entityPool, String className) {
		return new SerializedClass(className, 1, CLASS_DESCRIPTION_FLAGS, entityPool);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy