/* * Copyright (C) 2011 Rhegium Team * * 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.rhegium.internal.serialization; import java.io.DataInput; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import org.rhegium.api.serialization.Attribute; import org.rhegium.api.serialization.AttributeAccessorException; import org.rhegium.api.serialization.accessor.AccessorService; public final class AttributeDescriptor { private final AccessorService accessorService; private final Attribute attribute; private final boolean privateField; private final boolean optional; private final Field field; public AttributeDescriptor(Attribute attribute, Field field, AccessorService accessorService) { this.accessorService = accessorService; this.attribute = attribute; this.field = field; this.privateField = Modifier.isPrivate(field.getModifiers()); this.optional = attribute.optional(); AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { AttributeDescriptor.this.field.setAccessible(true); return null; } }); } public Attribute getProtocolAttribute() { return attribute; } public Field getField() { return field; } public Class<?> getType() { return field.getType(); } public boolean isPrivateField() { return privateField; } public boolean isOptional() { return optional; } public int getSerializedSize(InputStream stream) { // If attribute type is a string or a byte- / Byte-array we need to read // the size from stream without killing the real position if (String.class.isAssignableFrom(getType()) || byte[].class.isAssignableFrom(getType()) || Byte[].class.isAssignableFrom(getType())) { // If stream does not support position marking, we cannot go on at // this point so inform the user if (!stream.markSupported()) { throw new AttributeAccessorException(String.format( "Cannot analyse stream for string or array size on attribute %s", this)); } try { // Mark actual position inside stream // Actual 4 bytes should be enough for reading int length but... stream.mark(8); // Read Strings length int length = ((DataInput) stream).readInt(); // Reset stream to meet deserializers expectations stream.reset(); return length; } catch (IOException e) { return -1; } } return accessorService.getSerializedSize(getType()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((field == null) ? 0 : field.hashCode()); result = prime * result + ((attribute == null) ? 0 : attribute.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AttributeDescriptor other = (AttributeDescriptor) obj; if (field == null) { if (other.field != null) return false; } else if (!field.equals(other.field)) return false; if (attribute == null) { if (other.attribute != null) return false; } else if (!attribute.equals(other.attribute)) return false; return true; } @Override public String toString() { return String.format("Attribute [attribute=%s, field=%s]", attribute, field); } }