org.jboss.marshalling.reflect.SerializableField Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.jboss.marshalling.reflect;
import static java.lang.System.getSecurityManager;
import static java.security.AccessController.doPrivileged;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.jboss.marshalling._private.GetUnsafeAction;
import org.jboss.marshalling.util.Kind;
import sun.misc.Unsafe;
/**
* Reflection information about a field on a serializable class.
*/
public final class SerializableField {
static final Unsafe unsafe = getSecurityManager() == null ? GetUnsafeAction.INSTANCE.run() : doPrivileged(GetUnsafeAction.INSTANCE);
// the type of the field itself
private final Class> type;
private final Field field;
private final String name;
private final boolean unshared;
private final Kind kind;
private final long fieldOffset;
private final int recordComponentIndex;
public SerializableField(Class> type, String name, boolean unshared) {
this(type, name, unshared, null, null);
}
SerializableField(Class> type, String name, boolean unshared, final Field field, RecordComponent recordComponent) {
assert field == null || (field.getModifiers() & Modifier.STATIC) == 0 && ! field.getDeclaringClass().isArray();
this.type = type;
this.name = name;
this.unshared = unshared;
this.field = field;
fieldOffset = field == null || recordComponent != null? -1 : unsafe.objectFieldOffset(field);
if (field != null) {
// verify field information
if (field.getType() != type) {
throw new IllegalStateException("Constructed a serializable field with the wrong type (field type is " + field.getType() + ", our type is " + type + ")");
}
if (! field.getName().equals(name)) {
throw new IllegalStateException("Constructed a serializable field with the wrong name (field name is " + field.getName() + ", our name is " + name + ")");
}
}
if (recordComponent != null) {
if (field == null) {
throw new IllegalStateException("Record component field not passed for name " + recordComponent.getName());
}
if (recordComponent.getType() != type) {
throw new IllegalStateException("Record component field with the wrong type (field type is " + recordComponent.getType() + ", our type is " + type + ")");
}
if (! recordComponent.getName().equals(name)) {
throw new IllegalStateException("Record component field with the wrong name (field name is " + recordComponent.getName() + ", our name is " + name + ")");
}
this.recordComponentIndex = recordComponent.getIndex();
} else {
this.recordComponentIndex = -1;
}
// todo - see if a small Map is faster
if (type == boolean.class) {
kind = Kind.BOOLEAN;
} else if (type == byte.class) {
kind = Kind.BYTE;
} else if (type == short.class) {
kind = Kind.SHORT;
} else if (type == int.class) {
kind = Kind.INT;
} else if (type == long.class) {
kind = Kind.LONG;
} else if (type == char.class) {
kind = Kind.CHAR;
} else if (type == float.class) {
kind = Kind.FLOAT;
} else if (type == double.class) {
kind = Kind.DOUBLE;
} else {
kind = Kind.OBJECT;
}
}
/**
* Get the reflection {@code Field} for this serializable field. The resultant field will be accessible.
*
* @return the reflection field
* @deprecated As of Java 9, accessible fields are generally disallowed; use the {@code #setXXX(Object,value)} methods instead.
*/
@Deprecated
public Field getField() {
return field;
}
/**
* Determine if this object may be used to get or set an object field value.
*
* @return {@code true} if this object may be used to get or set an object field value, {@code false} otherwise
*/
public boolean isAccessible() {
return field != null;
}
/**
* Get the name of the field.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Determine whether this field is marked as "unshared".
*
* @return {@code true} if the field is unshared
*/
public boolean isUnshared() {
return unshared;
}
/**
* Get the kind of field.
*
* @return the kind
*/
public Kind getKind() {
return kind;
}
/**
* Get the field type.
*
* @return the field type
*/
public Class> getType() throws ClassNotFoundException {
return type;
}
/**
* Set the boolean value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setBoolean(Object instance, boolean value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != boolean.class) {
throw new ClassCastException();
}
unsafe.putBoolean(instance, fieldOffset, value);
}
/**
* Set the char value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setChar(Object instance, char value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != char.class) {
throw new ClassCastException();
}
unsafe.putChar(instance, fieldOffset, value);
}
/**
* Set the byte value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setByte(Object instance, byte value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != byte.class) {
throw new ClassCastException();
}
unsafe.putByte(instance, fieldOffset, value);
}
/**
* Set the short value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setShort(Object instance, short value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != short.class) {
throw new ClassCastException();
}
unsafe.putShort(instance, fieldOffset, value);
}
/**
* Set the integer value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setInt(Object instance, int value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != int.class) {
throw new ClassCastException();
}
unsafe.putInt(instance, fieldOffset, value);
}
/**
* Set the long value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setLong(Object instance, long value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != long.class) {
throw new ClassCastException();
}
unsafe.putLong(instance, fieldOffset, value);
}
/**
* Set the float value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setFloat(Object instance, float value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != float.class) {
throw new ClassCastException();
}
unsafe.putFloat(instance, fieldOffset, value);
}
/**
* Set the double value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setDouble(Object instance, double value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != double.class) {
throw new ClassCastException();
}
unsafe.putDouble(instance, fieldOffset, value);
}
/**
* Set the object value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @param value the value to set
* @throws ClassCastException if {@code instance} or the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public void setObject(Object instance, Object value) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
final Class> fieldType = field.getType();
if (fieldType.isPrimitive()) {
throw new ClassCastException();
}
fieldType.cast(value);
unsafe.putObject(instance, fieldOffset, value);
}
/**
* Get the boolean value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public boolean getBoolean(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != boolean.class) {
throw new ClassCastException();
}
return unsafe.getBoolean(instance, fieldOffset);
}
/**
* Get the char value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public char getChar(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != char.class) {
throw new ClassCastException();
}
return unsafe.getChar(instance, fieldOffset);
}
/**
* Get the byte value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public byte getByte(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != byte.class) {
throw new ClassCastException();
}
return unsafe.getByte(instance, fieldOffset);
}
/**
* Get the short value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public short getShort(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != short.class) {
throw new ClassCastException();
}
return unsafe.getShort(instance, fieldOffset);
}
/**
* Get the integer value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public int getInt(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != int.class) {
throw new ClassCastException();
}
return unsafe.getInt(instance, fieldOffset);
}
/**
* Get the long value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public long getLong(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != long.class) {
throw new ClassCastException();
}
return unsafe.getLong(instance, fieldOffset);
}
/**
* Get the float value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public float getFloat(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != float.class) {
throw new ClassCastException();
}
return unsafe.getFloat(instance, fieldOffset);
}
/**
* Get the double value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public double getDouble(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType() != double.class) {
throw new ClassCastException();
}
return unsafe.getDouble(instance, fieldOffset);
}
/**
* Get the object value of this field on the given object instance.
*
* @param instance the object instance (must not be {@code null}, must be of the correct type)
* @return the value of the field
* @throws ClassCastException if the field is not of the correct type
* @throws IllegalArgumentException if this instance has no reflection field set on it
*/
public Object getObject(Object instance) throws ClassCastException, IllegalArgumentException {
if (instance == null) {
throw new IllegalArgumentException("instance is null");
}
if (field == null) {
throw new IllegalArgumentException();
}
field.getDeclaringClass().cast(instance);
if (field.getType().isPrimitive()) {
throw new ClassCastException();
}
return unsafe.getObject(instance, fieldOffset);
}
/**
* Read the field value from the stream.
*
* @param instance the instance
* @param input the source stream
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a class could not be loaded
*/
public void readFrom(Object instance, ObjectInput input) throws IOException, ClassNotFoundException {
switch (kind) {
case BOOLEAN:
setBoolean(instance, input.readBoolean());
break;
case BYTE:
setByte(instance, input.readByte());
break;
case CHAR:
setChar(instance, input.readChar());
break;
case DOUBLE:
setDouble(instance, input.readDouble());
break;
case FLOAT:
setFloat(instance, input.readFloat());
break;
case INT:
setInt(instance, input.readInt());
break;
case LONG:
setLong(instance, input.readLong());
break;
case SHORT:
setShort(instance, input.readShort());
break;
case OBJECT:
setObject(instance, input.readObject());
break;
default:
throw new IllegalStateException();
}
}
public void writeTo(Object instance, ObjectOutput output) throws IOException {
switch (kind) {
case BOOLEAN:
output.writeBoolean(getBoolean(instance));
break;
case BYTE:
output.writeByte(getByte(instance));
break;
case CHAR:
output.writeChar(getChar(instance));
break;
case DOUBLE:
output.writeDouble(getDouble(instance));
break;
case FLOAT:
output.writeFloat(getFloat(instance));
break;
case INT:
output.writeInt(getInt(instance));
break;
case LONG:
output.writeLong(getLong(instance));
break;
case SHORT:
output.writeShort(getShort(instance));
break;
case OBJECT:
output.writeObject(getObject(instance));
break;
default:
throw new IllegalStateException();
}
}
/**
* Getter for the record component value.
* @param obj The object to obtain the record component value
* @return The record component value for this obj
*/
public Object getRecordComponentValue(Object obj) {
if (recordComponentIndex == -1) {
return null;
}
return JDKSpecific.getRecordComponentValue(obj, name, type);
}
/**
* Getter for the record component index.
* @return The record component index or -1
*/
public int getRecordComponentIndex() {
return recordComponentIndex;
}
/**
* A record component, which has a name, type and index.
*/
static final class RecordComponent {
private final String name;
private final Class> type;
private final int index;
RecordComponent(String name, Class> type, int index) {
this.name = name;
this.type = type;
this.index = index;
}
public String getName() {
return name;
}
public Class> getType() {
return type;
}
public int getIndex() {
return index;
}
}
}