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

org.bimserver.shared.HashMapVirtualObject Maven / Gradle / Ivy

Go to download

Base project for BIMserver plugin development. Some plugins mights also need the Shared library

The newest version!
package org.bimserver.shared;

/******************************************************************************
 * Copyright (C) 2009-2016  BIMserver.org
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see {@literal}.
 *****************************************************************************/

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bimserver.BimserverDatabaseException;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.models.ifc2x3tc1.Tristate;
import org.bimserver.plugins.deserializers.DatabaseInterface;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;

public class HashMapVirtualObject extends AbstractHashMapVirtualObject implements VirtualObject {
	private static final Logger LOGGER = LoggerFactory.getLogger(VirtualObject.class);
	private final Map map = new HashMap<>();
	private EClass eClass;
	private long oid;
	private QueryContext reusable;
	private Map useForSerializationFeatures = new HashMap<>();
	
	public HashMapVirtualObject(QueryContext reusable, EClass eClass) {
		this.reusable = reusable;
		this.eClass = eClass;
		this.oid = reusable.getDatabaseInterface().newOid(eClass);
	}

	public void eUnset(EStructuralFeature feature) {
		map.remove(feature);
	}

	public void setAttribute(EStructuralFeature feature, Object val) {
		map.put(feature, val);
	}

	public Object eGet(EStructuralFeature feature) {
		return map.get(feature);
	}

	public Object get(String featureName) {
		EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(featureName);
		return map.get(eStructuralFeature);
	}
	
	public boolean eIsSet(EStructuralFeature feature) {
		return map.containsKey(feature);
	}
	
	public EClass eClass() {
		return eClass;
	}
	
	private int getWrappedValueSize(Object val, EReference eReference) {
		if (val == null) {
			return 2;
		}
		if (val instanceof VirtualObject) {
			VirtualObject eObject = (VirtualObject) val;
			if (eReference.getEAnnotation("twodimensionalarray") != null) {
				int refSize = 4;
				EStructuralFeature eStructuralFeature = eObject.eClass().getEStructuralFeature("List");
				List l = (List)eObject.eGet(eStructuralFeature);
				for (Object o : l) {
					if (o instanceof VirtualObject) {
						refSize += 8;
					} else {
						refSize += getPrimitiveSize((EDataType) eStructuralFeature.getEType(), o);
					}
				}
				return refSize;
			} else if (eObject.eClass().getEAnnotation("wrapped") != null) {
				VirtualObject wrappedValue = (VirtualObject) val;
				EStructuralFeature wrappedValueFeature = wrappedValue.eClass().getEStructuralFeature("wrappedValue");
				Object wrappedVal = eObject.eGet(wrappedValueFeature);
				return 2 + getPrimitiveSize((EDataType) wrappedValueFeature.getEType(), wrappedVal);
			} else {
				return 8;
			}
		} else if (val instanceof WrappedVirtualObject) {
			WrappedVirtualObject wrappedVirtualObject = (WrappedVirtualObject)val;
			return wrappedVirtualObject.getSize();
		} else if (val instanceof Long) {
			return 8;
		} else {
			throw new RuntimeException("Programming error, should not happen " + val);
		}
	}

	private int getExactSize(VirtualObject virtualObject) {
		int size = 0;

		for (EStructuralFeature eStructuralFeature : eClass().getEAllStructuralFeatures()) {
			if (getPackageMetaData().useForDatabaseStorage(eClass, eStructuralFeature)) {
				if (!useUnsetBit(eStructuralFeature)) {
					Object val = eGet(eStructuralFeature);
					if (eStructuralFeature instanceof EAttribute) {
						EAttribute eAttribute = (EAttribute) eStructuralFeature;
						if (eAttribute.isMany()) {
							size += 4;
							for (Object v : ((List) val)) {
								size += getPrimitiveSize(eAttribute.getEAttributeType(), v);
							}
						} else {
							size += getPrimitiveSize(eAttribute.getEAttributeType(), val);
						}
					} else if (eStructuralFeature instanceof EReference) {
						EReference eReference = (EReference) eStructuralFeature;
						if (eReference.isMany()) {
							size += 4;
							for (Object v : ((List) val)) {
								size += getWrappedValueSize(v, eReference);
							}
						} else {
							size += getWrappedValueSize(val, eReference);
						}
					}
				}
			}
		}

		size += getPackageMetaData().getUnsettedLength(eClass);

		return size;
	}
	
	private boolean useUnsetBit(EStructuralFeature feature) {
		// TODO non-unsettable boolean values can also be stored in these bits
		Object value = eGet(feature);
		if (feature.isUnsettable()) {
			if (!eIsSet(feature)) {
				return true;
			}
		} else {
			if (feature.isMany() && (value == null || ((List)value).isEmpty())) {
				return true;
			}
			if (feature.getDefaultValue() == value || (feature.getDefaultValue() != null && feature.getDefaultValue().equals(value))) {
				return true;
			}
		}
		return false;
	}

	public long getOid() {
		return oid;
	}
	
	public ByteBuffer write() throws BimserverDatabaseException {
		int bufferSize = getExactSize(this);
		ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
		byte[] unsetted = new byte[getPackageMetaData().getUnsettedLength(eClass)];
		int fieldCounter = 0;
		for (EStructuralFeature feature : eClass().getEAllStructuralFeatures()) {
			if (getPackageMetaData().useForDatabaseStorage(eClass, feature)) {
				if (useUnsetBit(feature)) {
					unsetted[fieldCounter / 8] |= (1 << (fieldCounter % 8));
				}
				fieldCounter++;
			}
		}
		buffer.put(unsetted);
		
		EClass eClass = getDatabaseInterface().getEClassForOid(getOid());
		if (!eClass.isSuperTypeOf(eClass())) {
			throw new BimserverDatabaseException("Object with oid " + getOid() + " is a " + eClass().getName() + " but it's cid-part says it's a " + eClass.getName());
		}

		for (EStructuralFeature feature : eClass().getEAllStructuralFeatures()) {
			if (getPackageMetaData().useForDatabaseStorage(eClass, feature)) {
				if (!useUnsetBit(feature)) {
					if (feature.isMany()) {
						writeList(this, buffer, getPackageMetaData(), feature);
					} else {
						Object value = eGet(feature);
						if (feature.getEType() instanceof EEnum) {
							if (value == null) {
								buffer.putInt(-1);
							} else {
								EEnum eEnum = (EEnum) feature.getEType();
								EEnumLiteral eEnumLiteral = eEnum.getEEnumLiteralByLiteral(((Enum) value).toString());
								if (eEnumLiteral != null) {
									buffer.putInt(eEnumLiteral.getValue());
								} else {
									LOGGER.error(((Enum) value).toString() + " not found");
									buffer.putInt(-1);
								}
							}
						} else if (feature.getEType() instanceof EClass) {
							if (value == null) {
								buffer.order(ByteOrder.LITTLE_ENDIAN);
								buffer.putShort((short) -1);
								buffer.order(ByteOrder.BIG_ENDIAN);
							} else if (value instanceof VirtualObject) {
								writeWrappedValue(getPid(), getRid(), (VirtualObject) value, buffer, getPackageMetaData());
							} else if (value instanceof WrappedVirtualObject) {
								writeWrappedValue(getPid(), getRid(), (WrappedVirtualObject) value, buffer, getPackageMetaData());
							} else {
								long referencedOid = (Long)value;
								writeReference(referencedOid, buffer, feature);
							}
						} else if (feature.getEType() instanceof EDataType) {
							writePrimitiveValue(feature, value, buffer);
						}
					}
				}
			}
		}
		if (buffer.position() != bufferSize) {
			throw new BimserverDatabaseException("Value buffer sizes do not match for " + eClass().getName() + " " + buffer.position() + "/" + bufferSize);
		}
		return buffer;
	}
	
	private PackageMetaData getPackageMetaData() {
		return reusable.getPackageMetaData();
	}

	private DatabaseInterface getDatabaseInterface() {
		return reusable.getDatabaseInterface();
	}

	public int getPid() {
		return reusable.getPid();
	}
	
	public int getRid() {
		return reusable.getRid();
	}
	
	private void writeWrappedValue(int pid, int rid, VirtualObject wrappedValue, ByteBuffer buffer, PackageMetaData packageMetaData) throws BimserverDatabaseException {
		EStructuralFeature eStructuralFeature = wrappedValue.eClass().getEStructuralFeature("wrappedValue");
		Short cid = getDatabaseInterface().getCidOfEClass(wrappedValue.eClass());
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		buffer.putShort((short) -cid);
		buffer.order(ByteOrder.BIG_ENDIAN);
		writePrimitiveValue(eStructuralFeature, wrappedValue.eGet(eStructuralFeature), buffer);
		if (wrappedValue.eClass().getName().equals("IfcGloballyUniqueId")) {
			EClass eClass = packageMetaData.getEClass("IfcGloballyUniqueId");
			if (wrappedValue.getOid() == -1) {
				((VirtualObject) wrappedValue).setOid(getDatabaseInterface().newOid(eClass));
			}
			getDatabaseInterface().save(wrappedValue);
		}
	}

	private void writeWrappedValue(int pid, int rid, WrappedVirtualObject wrappedValue, ByteBuffer buffer, PackageMetaData packageMetaData) throws BimserverDatabaseException {
		Short cid = getDatabaseInterface().getCidOfEClass(wrappedValue.eClass());
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		buffer.putShort((short) -cid);
		buffer.order(ByteOrder.BIG_ENDIAN);
		for (EStructuralFeature eStructuralFeature : wrappedValue.eClass().getEAllStructuralFeatures()) {
			writePrimitiveValue(eStructuralFeature, wrappedValue.eGet(eStructuralFeature), buffer);
		}
	}
	
	public void setOid(long oid) {
		this.oid = oid;
	}

	private void writeReference(long referenceOid, ByteBuffer buffer, EStructuralFeature feature) throws BimserverDatabaseException {
		if (referenceOid < 0) {
			throw new BimserverDatabaseException("Writing a reference with oid " + referenceOid + ", this is not supposed to happen, referenced: " + referenceOid + " " + referenceOid + " from " + getOid() + " " + this);
		}
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		buffer.putLong(referenceOid);
		buffer.order(ByteOrder.BIG_ENDIAN);
	}

	@SuppressWarnings("rawtypes")
	private void writeList(VirtualObject virtualObject, ByteBuffer buffer, PackageMetaData packageMetaData, EStructuralFeature feature) throws BimserverDatabaseException {
		if (feature.getEType() instanceof EEnum) {
			// Aggregate relations to enums never occur... at this
			// moment
		} else if (feature.getEType() instanceof EClass) {
			List list = (List) virtualObject.eGet(feature);
			buffer.putInt(list.size());
			for (Object o : list) {
				if (o == null) {
					buffer.order(ByteOrder.LITTLE_ENDIAN);
					buffer.putShort((short) -1);
					buffer.order(ByteOrder.BIG_ENDIAN);
				} else {
					if (o instanceof VirtualObject) {
						VirtualObject listObject = (VirtualObject)o;
						if (feature.getEAnnotation("twodimensionalarray") != null) {
							EStructuralFeature lf = listObject.eClass().getEStructuralFeature("List");
							writeList(listObject, buffer, packageMetaData, lf);
						} else {
							if (listObject.eClass().getEAnnotation("wrapped") != null || listObject.eClass().getEStructuralFeature("wrappedValue") != null) {
								writeWrappedValue(getPid(), getRid(), listObject, buffer, packageMetaData);
							}
						}
					} else {
						long listObjectOid = (Long) o;
						writeReference(listObjectOid, buffer, feature);
					}
				}
			}
		} else if (feature.getEType() instanceof EDataType) {
			List list = (List) eGet(feature);
			buffer.putInt(list.size());
			for (Object o : list) {
				writePrimitiveValue(feature, o, buffer);
			}
		}
	}
	
	private void writePrimitiveValue(EStructuralFeature feature, Object value, ByteBuffer buffer) throws BimserverDatabaseException {
		EClassifier type = feature.getEType();
		if (type == EcorePackage.eINSTANCE.getEString()) {
			if (value == null) {
				buffer.putInt(-1);
			} else {
				String stringValue = (String) value;
				byte[] bytes = stringValue.getBytes(Charsets.UTF_8);
				if (bytes.length > Integer.MAX_VALUE) {
					throw new BimserverDatabaseException("String value too long (max length is " + Integer.MAX_VALUE + ")");
				}
				buffer.putInt(bytes.length);
				buffer.put(bytes);
			}
		} else if (type == EcorePackage.eINSTANCE.getEInt() || type == EcorePackage.eINSTANCE.getEIntegerObject()) {
			if (value == null) {
				buffer.putInt(0);
			} else {
				buffer.putInt((Integer) value);
			}
		} else if (type == EcorePackage.eINSTANCE.getEDouble() || type == EcorePackage.eINSTANCE.getEDoubleObject()) {
			if (value == null) {
				buffer.putDouble(0D);
			} else {
				buffer.putDouble((Double) value);
			}
		} else if (type == EcorePackage.eINSTANCE.getEFloat() || type == EcorePackage.eINSTANCE.getEFloatObject()) {
			if (value == null) {
				buffer.putFloat(0F);
			} else {
				buffer.putFloat((Float) value);
			}
		} else if (type == EcorePackage.eINSTANCE.getELong() || type == EcorePackage.eINSTANCE.getELongObject()) {
			if (value == null) {
				buffer.putLong(0L);
			} else {
				buffer.putLong((Long) value);
			}
		} else if (type == EcorePackage.eINSTANCE.getEBoolean() || type == EcorePackage.eINSTANCE.getEBooleanObject()) {
			if (value == null) {
				buffer.put((byte) 0);
			} else {
				buffer.put(((Boolean) value) ? (byte) 1 : (byte) 0);
			}
		} else if (type == EcorePackage.eINSTANCE.getEDate()) {
			if (value == null) {
				buffer.putLong(-1L);
			} else {
				buffer.putLong(((Date) value).getTime());
			}
		} else if (type.getName().equals("Tristate")) {
			if (value == null) {
				buffer.putInt(Tristate.UNDEFINED.getValue());
			} else {
				Enumerator eEnumLiteral = (Enumerator) value;
				buffer.putInt(eEnumLiteral.getValue());
			}
		} else if (value instanceof Enumerator) {
			Enumerator eEnumLiteral = (Enumerator) value;
			buffer.putInt(eEnumLiteral.getValue());
		} else if (type == EcorePackage.eINSTANCE.getEByteArray()) {
			if (value == null) {
				buffer.putInt(0);
			} else {
				byte[] bytes = (byte[]) value;
				buffer.putInt(bytes.length);
				buffer.put(bytes);
			}
		} else {
			throw new RuntimeException("Unsupported type " + type.getName());
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void setListItem(EStructuralFeature structuralFeature, int index, Object value) {
		List list = getOrCreateList(structuralFeature, index + 1);
		list.set(index, value);
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void setListItemReference(EStructuralFeature structuralFeature, int index, EClass referenceEClass, Long referencedOid, int bufferPosition) {
		List list = getOrCreateList(structuralFeature, index + 1);
		list.set(index, referencedOid);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private List getOrCreateList(EStructuralFeature structuralFeature, int minSize) {
		List list = (List) map.get(structuralFeature);
		if (list == null) {
			list = new ArrayList(minSize == -1 ? 0 : minSize);
			map.put(structuralFeature, list);
		}
		while (list.size() < minSize) {
			list.add(null);
		}
		return list;
	}

	public void save() throws BimserverDatabaseException {
		getDatabaseInterface().save(this);
	}

	@Override
	public int reserveSpaceForReference(EStructuralFeature feature) {
		// Not relevant for HashMap based implementation
		return -1;
	}

	@Override
	public int reserveSpaceForListReference() {
		// Not relevant for HashMap based implementation
		return -1;
	}
	
	@Override
	public void startList(EStructuralFeature feature) {
		// Not relevant for HashMap based implementation		
	}

	@Override
	public void setReference(EStructuralFeature feature, long referenceOid, int bufferPosition) throws BimserverDatabaseException {
		map.put(feature, referenceOid);
	}

	@Override
	public void endList() {
		// Not relevant for HashMap based implementation		
	}

	public long getRoid() {
		return reusable.getRoid();
	}

	public boolean has(String key) {
		EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(key);
		return map.containsKey(eStructuralFeature) && map.get(eStructuralFeature) != null;
	}

	@SuppressWarnings("unchecked")
	public boolean useFeatureForSerialization(EStructuralFeature feature, int index) {
		if (feature instanceof EAttribute) {
			return true;
		}
		if (useForSerializationFeatures.containsKey(feature)) {
			Object object = useForSerializationFeatures.get(feature);
			if (object instanceof Set) {
				Set set = (Set) object;
				if (set.contains(index)) {
					return true;
				}
			} else {
				return object == Boolean.TRUE;
			}
		}
		return false;
	}
	
	public boolean useFeatureForSerialization(EStructuralFeature feature) {
		if (feature instanceof EAttribute) {
			return true;
		}
		return useForSerializationFeatures.containsKey(feature);
	}

	public void addUseForSerialization(EStructuralFeature eStructuralFeature) {
		if (eStructuralFeature.getEContainingClass().isSuperTypeOf(eClass)) {
			useForSerializationFeatures.put(eStructuralFeature, true);
		} else {
			throw new IllegalArgumentException(eStructuralFeature.getName() + " does not exist in " + eClass.getName());
		}
	}
	
	@SuppressWarnings("unchecked")
	public void addUseForSerialization(EStructuralFeature eStructuralFeature, int index) {
		if (eStructuralFeature.getEContainingClass().isSuperTypeOf(eClass)) {
			Set set = (Set) useForSerializationFeatures.get(eStructuralFeature);
			if (set == null) {
				set = new HashSet<>();
				useForSerializationFeatures.put(eStructuralFeature, set);
			}
			set.add(index);
		} else {
			throw new IllegalArgumentException(eStructuralFeature.getName() + " does not exist in " + eClass.getName());
		}
	}

	public void saveOverwrite() throws BimserverDatabaseException {
		getDatabaseInterface().saveOverwrite(this);
	}

	@Override
	public void set(String name, Object val) throws BimserverDatabaseException {
		setAttribute(eClass.getEStructuralFeature(name), val);
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(eClass.getName() + "\n");
		for (EStructuralFeature eStructuralFeature : map.keySet()) {
			sb.append("\t" + eStructuralFeature.getName() + ": " + map.get(eStructuralFeature) + "\n");
		}
		return sb.toString();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy