org.conqat.lib.commons.serialization.classes.SerializedClass Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-lib-commons Show documentation
Show all versions of teamscale-lib-commons Show documentation
Provides common utility functions
/*
* 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);
}
}