/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.mdr.storagemodel; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import javax.jmi.model.Attribute; import javax.jmi.model.ModelElement; import javax.jmi.model.TypedElement; import javax.jmi.reflect.RefEnum; import javax.jmi.reflect.RefObject; import org.netbeans.mdr.handlers.BaseObjectHandler; import org.netbeans.mdr.handlers.EnumImpl; import org.netbeans.mdr.persistence.StorageException; import org.netbeans.mdr.util.DebugException; import org.netbeans.mdr.util.IOUtils; import StorableClass.AttributeDescriptor; /** * Helper class, which handles (de)serialization of StorableFeatured * attribute values, according to its metaclass. It acts as a replacement * for some util.IOUtils features, since they don't need to be exported * outside the StorageModel package. (De)serialization of Streamables *is* a * part of the storage model, after all. * * @author Svatopluk Dedic */ class AttributeIOHelper { static final int T_BOOLEAN = 1; static final int T_BYTE = T_BOOLEAN + 1; static final int T_CHAR = T_BYTE + 1; static final int T_DOUBLE = T_CHAR + 1; static final int T_FLOAT = T_DOUBLE + 1; static final int T_INTEGER = T_FLOAT + 1; static final int T_LONG = T_INTEGER + 1; static final int T_SHORT = T_LONG + 1; static final int T_OBJECT = T_SHORT + 1; static final int T_STRING = T_OBJECT + 1; static final int T_ENUM = T_STRING + 1; /** * Special value used for enums during the bootsequence. Reflective information * is not able/reliable at that time. */ static final int T_BOOT_ENUM = 0xfd; /** * Virtually any datatypes during the boot sequence. Should be decided * at runtime. */ static final int T_BOOT = 0xfe; static final int T_UNKNOWN = 0xff; static final int T_FLAG_MULTIVALUED = 0x1000; static final int T_FLAG_IMMUTABLE = 0x2000; static final int T_FLAG_UNIQUE = 0x4000; static final int T_FLAGS = (T_FLAG_MULTIVALUED | T_FLAG_IMMUTABLE | T_FLAG_UNIQUE); static final int T_TYPES = 0xff; /** * Helper method for StorableClass deserialization, it registers any * enumeration with the runtime so its values can be found quickly. * [PENDING] the registration is "eager", obtaining the values. If * the death rate of StorableClass is high, the map fill may be done * when an instance is first (de)serialized. */ static final void readResolveHelper(StorableClass instance, AttributeDescriptor[] descriptors) { if (descriptors == null) return; for (int i = 0; i < descriptors.length; i++) { AttributeDescriptor desc = descriptors[i]; int tag = desc.getSerialCode(); if ((tag & T_TYPES) == T_ENUM) { initEnumerationIndex(desc, instance); } } } private static boolean initEnumerationIndex(AttributeDescriptor desc, StorableFeatured f) { String typeName = (String)f.getMdrStorage().values().resolve( desc.getTypeIndex()); int enumID = EnumImpl.initEnumeration(typeName, f.getMdrStorage(), desc.getMofId()); desc.setEnumerationID(enumID); return enumID >= 0; } /** * Finalizes definition of a ClassProxy. The method is intended to be run * from within the constructor of a StorableClass. * It runs through AttributeDescriptors, registers enumerations and assigns * serialCodes to individual descriptors. * @param instance instance of the ClassProxy (StorableClass) * @param descriptors array of descriptors to finalize */ static final void finalizeClassProxy(StorableClass instance, AttributeDescriptor[] descriptors) { StorablePackage pack = null; boolean isBooting = instance.getMdrStorage().isBooting(); for (int i = 0; i < descriptors.length; i++) { AttributeDescriptor desc = descriptors[i]; if (isBooting) { desc.setSerialCode(T_BOOT); continue; } int tag; Class type = desc.getType(); if (type == Integer.class) { tag = T_INTEGER; } else if (type == String.class) { tag = T_STRING; } else if (type == Boolean.class) { tag = T_BOOLEAN; } else if (RefObject.class.isAssignableFrom(type)) { tag = T_OBJECT; } else if (RefEnum.class.isAssignableFrom(type)) { tag = T_ENUM; // special handling: register the enum & assign the registration // id: if (initEnumerationIndex(desc, instance)) { tag = T_ENUM; } else { tag = T_BOOT_ENUM; } } else if (type == Long.class) { tag = T_LONG; } else if (type == Character.class) { tag = T_CHAR; } else if (type == Short.class) { tag = T_SHORT; } else if (type == Float.class) { tag = T_FLOAT; } else if (type == Double.class) { tag = T_DOUBLE; } else { try { Object o = BaseObjectHandler.getHandler(instance.getMdrStorage().getObject(desc.getMofId())); String owner; if (o != null) { RefObject ro = ((Attribute)o).refImmediateComposite(); owner = ((ModelElement)ro).getName(); } else { owner = ""; } System.err.println("[MDR-IOHelper] Using legacy serialiation for attribute " + desc.getName() + " (MOFID: " + desc.getMofId() + ") on " + owner); } catch (StorageException ex) { ex.printStackTrace(); } tag = T_UNKNOWN; } if (desc.isMultivalued()) { tag |= T_FLAG_MULTIVALUED; if (!desc.isChangeable()) tag |= T_FLAG_IMMUTABLE; if (desc.isUnique()) tag |= T_FLAG_UNIQUE; } desc.setSerialCode(tag); } } /** * Writes attributes for the target StorableClass. This is the main entry * point for StorableFeatured serialization. * * @param featured the featured owning the attributes * @param values values to be stored. May be null, meaning that all attributes * have their default values. */ public static void writeAttributeValues(OutputStream ostream, StorableFeatured featured, Object[] values) throws IOException, StorageException { if (values == null) { IOUtils.writeInt(ostream, 0); return; } IOUtils.writeInt(ostream, values.length); StorableClass classproxy = featured.getClassProxy(); int count = values.length; for (int i = 0; i < count; i++) { writeValue(ostream, classproxy.getAttrDesc(i), featured, values[i]); } } /** * Reads values of attributes for a StorableObject instance. * @param istream the stream to read from * @param meta classproxy of the insteance being read * @param descs attribute descriptors, for fast access. This parameter may * be null, but in that case, the stream must contain no values. */ public static Object[] readAttributeValues(InputStream istream, StorableFeatured featured) throws IOException, StorageException { int size = IOUtils.readInt(istream); // special case: an empty instance was written -> return null as well. if (size == 0) return null; StorableClass classproxy = featured.getClassProxy(); AttributeDescriptor[] descs = classproxy.getAttrDescriptors(); if (descs == null) { throw new DebugException("Classproxy is uninitialized, but " + "the stream contains attr values"); } if (size != descs.length) { throw new DebugException("Attribute count mismatch (is: " + classproxy.getAttrCount() + " was: " + size); } Object[] values = new Object[size]; for (int i = 0; i < size; i++) { AttributeDescriptor desc = descs[i]; int serCode = desc.getSerialCode(); // first decision: is multivalued ? If so, which list will be // asked to deserialize that ? int multCode = (serCode & T_FLAGS); if (multCode > 0) { AttrImmutableList result; switch (serCode & T_FLAGS) { case T_FLAG_MULTIVALUED: case T_FLAG_MULTIVALUED | T_FLAG_UNIQUE: result = new AttrMutableList(); break; case T_FLAG_MULTIVALUED | T_FLAG_IMMUTABLE: result = new AttrImmutableList(); break; case T_FLAG_MULTIVALUED | T_FLAG_UNIQUE | T_FLAG_IMMUTABLE: result = new AttrImmutableUList(); break; default: throw new DebugException("Unknown multivalued attr tag: " + multCode); } result.read(istream, featured, desc, i); values[i] = result; } else { values[i] = readValue(istream, featured, desc, serCode); } } return values; } public static final List readList(InputStream istream, StorableFeatured owner, AttributeDescriptor desc) throws IOException, StorageException { int size = IOUtils.readInt(istream); List l = new ArrayList(size); int serialCode = desc.getSerialCode() & T_TYPES; for (int i = 0; i < size; i++) { l.add(readValue(istream, owner, desc, serialCode)); } return l; } private static Object readValue(InputStream istream, StorableFeatured owner, AttributeDescriptor desc, int code) throws IOException, StorageException { int serialCode = desc.getSerialCode(); switch (code) { case T_UNKNOWN: case T_BOOT: return IOUtils.read(istream, owner, desc.getType().getName()); case T_BOOLEAN: return IOUtils.readBoolean(istream) ? Boolean.TRUE : Boolean.FALSE; case T_BYTE: return new Byte((byte)istream.read()); case T_CHAR: return new Character((char)IOUtils.readInt(istream)); case T_DOUBLE: return new Double( Double.longBitsToDouble(IOUtils.readLong(istream)) ); case T_INTEGER: return new Integer(IOUtils.readInt(istream)); case T_LONG: return new Long(IOUtils.readLong(istream)); case T_FLOAT: return new Float( Float.intBitsToFloat(IOUtils.readInt(istream)) ); case T_SHORT: return new Short((short)IOUtils.readInt(istream)); case T_STRING: return IOUtils.readString(istream); case T_OBJECT: return BaseObjectHandler.getHandler( owner.getMdrStorage().getObject( IOUtils.readString(istream) )); case T_ENUM: return EnumImpl.decodeEnumInstance(desc.getEnumerationID(), IOUtils.readInt(istream)); default: throw new DebugException("Unhandled serialCode: " + code); } } /** * Serializes an attribute. Multivalued attributes are handled by delegating * to the List object. */ static final void writeValue(OutputStream ostream, AttributeDescriptor d, StorableFeatured owner, Object v) throws IOException { int serialCode = d.getSerialCode(); switch (serialCode & T_FLAGS) { case T_FLAG_MULTIVALUED: case T_FLAG_MULTIVALUED | T_FLAG_UNIQUE: case T_FLAG_MULTIVALUED | T_FLAG_IMMUTABLE: case T_FLAG_MULTIVALUED | T_FLAG_IMMUTABLE | T_FLAG_UNIQUE: ((AttrImmutableList)v).write(ostream, d, owner); break; case 0: writeValue(ostream, d, serialCode, owner, v); break; default: throw new DebugException("Unhandled serialCode: " + serialCode); } } static final void writeList(OutputStream ostream, List lst, StorableFeatured owner, AttributeDescriptor d) throws IOException { int size = lst.size(); IOUtils.writeInt(ostream, size); if (size == 0) return; int serialCode = d.getSerialCode() & T_TYPES; for (Iterator it = lst.iterator(); it.hasNext(); ) { writeValue(ostream, d, serialCode, owner, it.next()); } } /** * Serializes a single value. Still needs an AttributeDescriptor for a case * of serializing an Enum. */ static final void writeValue(OutputStream ostream, StorableClass.AttributeDescriptor d, int code, StorableFeatured owner, Object v) throws IOException { switch (code) { case T_BOOLEAN: ostream.write(((Boolean)v).booleanValue() ? 1 : 0); break; case T_BYTE: ostream.write(((Byte)v).byteValue()); break; case T_CHAR: IOUtils.writeInt(ostream, ((Character)v).charValue()); break; case T_SHORT: IOUtils.writeInt(ostream, ((Short)v).shortValue()); break; case T_INTEGER: IOUtils.writeInt(ostream, ((Integer)v).intValue()); break; case T_FLOAT: IOUtils.writeInt(ostream, Float.floatToIntBits(((Float)v).floatValue())); break; case T_DOUBLE: IOUtils.writeLong(ostream, Double.doubleToLongBits(((Double)v).doubleValue())); break; case T_LONG: IOUtils.writeLong(ostream, ((Long)v).longValue()); break; /** * These cases must handle null value(s) gracefully and write * them distinct from non-null values. */ case T_STRING: IOUtils.writeString(ostream, (String)v); break; case T_OBJECT: { IOUtils.writeString(ostream, ((RefObject)v).refMofId()); break; } case T_ENUM: // null enums are encoded as 0 IOUtils.writeInt(ostream, EnumImpl.encodeEnumInstance((RefEnum)v)); break; case T_BOOT: case T_UNKNOWN: IOUtils.write(ostream, v, owner.getMdrStorage()); break; default: throw new DebugException("Unhandled serialCode: " + code); } } }