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

org.bimserver.database.queries.DatabaseReadingStackFrame Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package org.bimserver.database.queries;

/******************************************************************************
 * Copyright (C) 2009-2019  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.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.bimserver.BimserverDatabaseException;
import org.bimserver.database.DatabaseSession.GetResult;
import org.bimserver.database.Record;
import org.bimserver.database.SearchingRecordIterator;
import org.bimserver.database.queries.om.CanInclude;
import org.bimserver.database.queries.om.Include;
import org.bimserver.database.queries.om.Include.TypeDef;
import org.bimserver.database.queries.om.QueryException;
import org.bimserver.database.queries.om.QueryPart;
import org.bimserver.database.queries.om.Reference;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.shared.HashMapVirtualObject;
import org.bimserver.shared.HashMapWrappedVirtualObject;
import org.bimserver.shared.QueryContext;
import org.bimserver.shared.WrappedVirtualObject;
import org.bimserver.utils.BinUtils;
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.eclipse.emf.ecore.impl.EEnumImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class DatabaseReadingStackFrame extends StackFrame implements ObjectProvidingStackFrame {
	private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseReadingStackFrame.class);
	private final QueryContext reusable;
	private final QueryObjectProvider queryObjectProvider;
	protected HashMapVirtualObject currentObject;
	private final QueryPart queryPart;

	public DatabaseReadingStackFrame(QueryContext reusable, QueryObjectProvider queryObjectProvider, QueryPart queryPart) {
		this.reusable = reusable;
		this.queryObjectProvider = queryObjectProvider;
		this.queryPart = queryPart;
	}
	
	public QueryContext getReusable() {
		return reusable;
	}
	
	@Override
	public HashMapVirtualObject getCurrentObject() {
		return currentObject;
	}
	
	public QueryObjectProvider getQueryObjectProvider() {
		return queryObjectProvider;
	}
	
	public PackageMetaData getPackageMetaData() {
		return reusable.getPackageMetaData();
	}
	
	public QueryPart getQueryPart() {
		return queryPart;
	}
	
	protected void processPossibleIncludes(HashMapVirtualObject object, EClass previousType, CanInclude canInclude) throws QueryException, BimserverDatabaseException {
		if (object != null && canInclude != null) {
			if (canInclude.hasReferences()) {
				for (Reference reference : canInclude.getReferences()) {
					processPossibleInclude(object, canInclude, reference.getInclude());
				}
			}
			if (canInclude.hasIncludes()) {
				for (Include include : canInclude.getIncludes()) {
					processPossibleInclude(object, canInclude, include);
				}
			}
			if (canInclude.isIncludeAllFields()) {
				for (EReference eReference : object.eClass().getEAllReferences()) {
					Include include = new Include(reusable.getPackageMetaData());
					include.addType(object.eClass(), false);
					include.addField(eReference.getName());
					if (canInclude.hasIncludes()) {
						for (Include include2 : canInclude.getIncludes()) {
							include.addInclude(include2);
						}
					}
					processPossibleInclude(object, canInclude, include);
				}
			}
			if (canInclude instanceof Include) {
				processPossibleInclude(object, null, (Include) canInclude);
			}
		}
	}

	protected void processPossibleInclude(HashMapVirtualObject object, CanInclude previousInclude, Include include) throws QueryException, BimserverDatabaseException {
		if (include.hasTypes()) {
			for (TypeDef filterClass : include.getTypes()) {
				// TODO also exclude the possibly excluded types here
				if (!filterClass.geteClass().isSuperTypeOf(object.eClass())) {
//					System.out.println(filterClass.getName() + " / " + currentObject.eClass().getName());
					// TODO too many times queries are spending time here
					return;
				}
			}
		}
		if (include.hasDirectFields()) {
			for (EReference eReference : include.getFieldsDirect()) {
				Object ref = object.get(eReference.getName());
				if (ref != null) {
					if (ref instanceof List) {
						for (Long r : (List)ref) {
							HashMapVirtualObject byOid = getByOid(r, true);
							int index = object.addDirectListReference(eReference, byOid);
							object.addUseForSerialization(eReference, index);
							processPossibleIncludes(byOid, byOid.eClass(), include);
						}
					} else {
						if (ref instanceof Long) {
							HashMapVirtualObject byOid = getByOid((Long)ref, true);
							if (byOid == null) {
								throw new BimserverDatabaseException("Object with oid " + ref + " not found (" + queryObjectProvider.getDatabaseSession().getEClassForOid((Long)ref).getName() + ")");
							}
							object.setDirectReference(eReference, byOid);
							object.addUseForSerialization(eReference);
							processPossibleIncludes(byOid, byOid.eClass(), include);
						} else {
							object.setDirectReference(eReference, (HashMapWrappedVirtualObject)ref);
						}
					}
				}
			}
		}
		if (include.hasFields() && !include.isExclude()) {
			for (EStructuralFeature eStructuralFeature : include.getFields()) {
				object.addUseForSerialization(eStructuralFeature);
			}
		}

		getQueryObjectProvider().push(new QueryIncludeStackFrame(getQueryObjectProvider(), getReusable(), previousInclude, include, object, queryPart));
	}
	
	public GetResult getMap(EClass originalQueryClass, EClass eClass, ByteBuffer buffer, int keyPid, long keyOid, int keyRid) throws BimserverDatabaseException {
		if (keyPid == getReusable().getPid()) {
			if (keyRid <= getReusable().getRid() && keyRid >= getReusable().getStopRid()) {
				if (!getQueryObjectProvider().hasRead(keyOid)) {
					if (buffer.capacity() == 1 && buffer.get(0) == -1) {
						buffer.position(buffer.position() + 1);
						return GetResult.CONTINUE_WITH_NEXT_OID;
						// deleted entity
					} else {
						 currentObject = convertByteArrayToObject(eClass, keyOid, buffer, keyRid);
					}
				}
				return GetResult.CONTINUE_WITH_NEXT_OID;
			} else {
				return GetResult.CONTINUE_WITH_NEXT_RECORD;
			}
		} else {
			return GetResult.STOP;
		}
	}
	
	protected HashMapVirtualObject convertByteArrayToObject(EClass eClass, long oid, ByteBuffer buffer, int rid) throws BimserverDatabaseException {
		try {
			int unsettedLength = getPackageMetaData().getUnsettedLength(eClass);
			
			byte[] unsetted = new byte[unsettedLength];
			buffer.get(unsetted);
			
			int fieldCounter = 0;
			
			HashMapVirtualObject idEObject = new HashMapVirtualObject(reusable, eClass, oid, new UUID(buffer.getLong(), buffer.getLong()));
			
			for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) {
				try {
					if (getPackageMetaData().useForDatabaseStorage(eClass, feature)) {
						boolean isUnsetted = (unsetted[fieldCounter / 8] & (1 << (fieldCounter % 8))) != 0;
						if (isUnsetted) {
							if (feature.isUnsettable()) {
								idEObject.eUnset(feature);
							} else if (feature.isMany()) {
								// do nothing
							} else if (feature.getDefaultValue() != null) {
								idEObject.setAttribute((EAttribute) feature, feature.getDefaultValue());
							}
						} else {
							Object newValue = null;
							if (feature.isMany()) {
								newValue = readList(idEObject, buffer, feature);
							} else {
								if (feature.getEType() instanceof EEnum) {
									int enumOrdinal = buffer.getInt();
									if (enumOrdinal == -1) {
										newValue = null;
									} else {
										EClassifier eType = feature.getEType();
										EEnumLiteral enumLiteral = ((EEnumImpl) eType).getEEnumLiteral(enumOrdinal);
										if (enumLiteral != null) {
											newValue = enumLiteral.getInstance();
										}
									}
								} else if (feature.getEType() instanceof EClass) {
									// EReference eReference = (EReference) feature;
									buffer.order(ByteOrder.LITTLE_ENDIAN);
									short cid = buffer.getShort();
									buffer.order(ByteOrder.BIG_ENDIAN);
									if (cid == -1) {
										// null, do nothing
									} else if (cid < 0) {
										// negative cid means value is embedded in
										// record
										EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass((short) (-cid));
										if (feature.getEAnnotation("dbembed") != null) {
											newValue = readEmbeddedValue(feature, buffer, referenceClass);
										} else {
											newValue = readWrappedValue(feature, buffer, referenceClass);
										}
									} else if (cid > 0) {
										// positive cid means value is reference to
										// other record
										EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass(cid);
										if (referenceClass == null) {
											throw new BimserverDatabaseException("No eClass found for cid " + cid);
										}
										buffer.position(buffer.position() - 2);
										newValue = readReference(buffer, feature, referenceClass);
										if ((Long)newValue != -1) {
											if (queryObjectProvider.hasReadOrIsGoingToRead(((Long)newValue)) || queryObjectProvider.hasReadOrIsGoingToRead(referenceClass)) {
												idEObject.addUseForSerialization(feature);
											}
										}
										// if (eReference.getEOpposite() != null &&
										// ((IdEObjectImpl)
										// newValue).isLoadedOrLoading()) {
										// newValue = null;
										// }
									}
								} else if (feature.getEType() instanceof EDataType) {
									newValue = readPrimitiveValue(feature.getEType(), buffer);
								}
								if (newValue != null) {
									if (feature instanceof EAttribute) {
										idEObject.setAttribute((EAttribute) feature, newValue);
									} else {
										if (newValue instanceof Long) {
											idEObject.setReference((EReference) feature, (Long)newValue);
										} else {
											idEObject.setReference((EReference) feature, (WrappedVirtualObject)newValue);
										}
									}
								}
							}
						}
						fieldCounter++;
					}
				} catch (StringIndexOutOfBoundsException e) {
					throw new BimserverDatabaseException("Reading " + eClass.getName() + "." + feature.getName(), e);
				} catch (BufferUnderflowException e) {
					throw new BimserverDatabaseException("Reading " + eClass.getName() + "." + feature.getName() + " " + buffer.capacity(), e);
				} catch (BufferOverflowException e) {
					throw new BimserverDatabaseException("Reading " + eClass.getName() + "." + feature.getName(), e);
				}
			}
			return idEObject;
		} catch (BufferUnderflowException e) {
			throw new BimserverDatabaseException("Reading " + eClass.getName(), e);
		} catch (BufferOverflowException e) {
			throw new BimserverDatabaseException("Reading " + eClass.getName(), e);
		}
	}
	
	private long readReference(ByteBuffer buffer, EStructuralFeature feature, EClass eClass) throws BimserverDatabaseException {
		if (buffer.capacity() == 1 && buffer.get(0) == -1) {
			buffer.position(buffer.position() + 1);
			return -1;
		}
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		long oid = buffer.getLong();
		buffer.order(ByteOrder.BIG_ENDIAN);
		return oid;
	}

	private HashMapWrappedVirtualObject readWrappedValue(EStructuralFeature feature, ByteBuffer buffer, EClass eClass) throws BimserverDatabaseException {
		EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature("wrappedValue");
		Object primitiveValue = readPrimitiveValue(eStructuralFeature.getEType(), buffer);
		HashMapWrappedVirtualObject eObject = new HashMapWrappedVirtualObject(eClass);
		eObject.setAttribute((EAttribute) eStructuralFeature, primitiveValue);
		if (eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEDouble() || eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEDoubleObject()) {
			EStructuralFeature strFeature = eClass.getEStructuralFeature("wrappedValueAsString");
			Object stringVal = readPrimitiveValue(EcorePackage.eINSTANCE.getEString(), buffer);
			eObject.setAttribute((EAttribute) strFeature, stringVal);
		}
		return eObject;
	}

	private HashMapWrappedVirtualObject readEmbeddedValue(EStructuralFeature feature, ByteBuffer buffer, EClass eClass) throws BimserverDatabaseException {
		HashMapWrappedVirtualObject eObject = new HashMapWrappedVirtualObject(eClass);
		for (EStructuralFeature eStructuralFeature : eClass.getEAllStructuralFeatures()) {
			if (eStructuralFeature.isMany()) {
			} else {
				if (eStructuralFeature.getEType() instanceof EDataType) {
					Object primitiveValue = readPrimitiveValue(eStructuralFeature.getEType(), buffer);
					eObject.setAttribute((EAttribute) eStructuralFeature, primitiveValue);
				} else {
					buffer.order(ByteOrder.LITTLE_ENDIAN);
					short cid = buffer.getShort();
					buffer.order(ByteOrder.BIG_ENDIAN);
					if (cid == -1) {
						// null, do nothing
					} else if (cid < 0) {
						// negative cid means value is embedded in
						// record
						EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass((short) (-cid));
						eObject.setReference((EReference) eStructuralFeature, readEmbeddedValue(eStructuralFeature, buffer, referenceClass));
					}
				}
			}
		}
		return eObject;
	}
	
	public Object readPrimitiveValue(EClassifier classifier, ByteBuffer buffer) {
		if (classifier == EcorePackage.eINSTANCE.getEString()) {
			int length = buffer.getInt();
			if (length != -1) {
				return BinUtils.readString(buffer, length);
			} else {
				return null;
			}
		} else if (classifier == EcorePackage.eINSTANCE.getEInt() || classifier == EcorePackage.eINSTANCE.getEIntegerObject()) {
			return buffer.getInt();
		} else if (classifier == EcorePackage.eINSTANCE.getELong() || classifier == EcorePackage.eINSTANCE.getELongObject()) {
			return buffer.getLong();
		} else if (classifier == EcorePackage.eINSTANCE.getEFloat() || classifier == EcorePackage.eINSTANCE.getEFloatObject()) {
			return buffer.getFloat();
		} else if (classifier == EcorePackage.eINSTANCE.getEDouble() || classifier == EcorePackage.eINSTANCE.getEDoubleObject()) {
			return buffer.getDouble();
		} else if (classifier == EcorePackage.eINSTANCE.getEShort() || classifier == EcorePackage.eINSTANCE.getEShortObject()) {
			return buffer.getShort();
		} else if (classifier == EcorePackage.eINSTANCE.getEBoolean() || classifier == EcorePackage.eINSTANCE.getEBooleanObject()) {
			return buffer.get() == 1;
		} else if (classifier == EcorePackage.eINSTANCE.getEDate()) {
			long val = buffer.getLong();
			if (val == -1L) {
				return null;
			}
			return new Date(val);
		} else if (classifier == EcorePackage.eINSTANCE.getEByteArray()) {
			int size = buffer.getInt();
			byte[] result = new byte[size];
			buffer.get(result);
			return result;
		} else if (classifier.getName().equals("Tristate")) {
			int ordinal = buffer.getInt();
			EEnum tristateEnum = getPackageMetaData().getEEnum("Tristate");
			return tristateEnum.getEEnumLiteral(ordinal).getInstance();
		} else if (classifier instanceof EEnum) {
			int ordinal = buffer.getInt();
			EEnum eEnum = (EEnum) classifier;
			return eEnum.getEEnumLiteral(ordinal).getInstance();
		} else {
			throw new RuntimeException("Unsupported type " + classifier.getName());
		}
	}
	
	private Object readList(HashMapVirtualObject idEObject, ByteBuffer buffer, EStructuralFeature feature) throws BimserverDatabaseException {
		if (feature.getEType() instanceof EEnum) {
		} else if (feature.getEType() instanceof EClass) {
			if (buffer.capacity() == 1 && buffer.get(0) == -1) {
				buffer.position(buffer.position() + 1);
			} else {
				int listSize = buffer.getInt();

//				// DEBUG
//				int limit = 2000;
//				if (listSize > limit) {
//					LOGGER.warn("List of size " + listSize + " > " + limit + ", probably an error");
//				}

				for (int i = 0; i < listSize; i++) {
					if (feature.getEAnnotation("twodimensionalarray") != null) {
						HashMapVirtualObject newObject = new HashMapVirtualObject(reusable, (EClass) feature.getEType());
						EClass eClass = (EClass)feature.getEType();
						EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature("List");
						
						buffer.order(ByteOrder.LITTLE_ENDIAN);
						short cid = buffer.getShort();
						buffer.order(ByteOrder.BIG_ENDIAN);
						EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass((short) (-cid));
						if (referenceClass == null) {
							throw new BimserverDatabaseException("No class found for cid " + (-cid));
						}
						
						Object result = readList(newObject, buffer, eStructuralFeature);
						if (eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEDouble() || eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEDoubleObject()) {
							result = readList(newObject, buffer, eClass.getEStructuralFeature("ListAsString"));
						}
						idEObject.setListItem(feature, i, newObject);
					} else {
						buffer.order(ByteOrder.LITTLE_ENDIAN);
						short cid = buffer.getShort();
						buffer.order(ByteOrder.BIG_ENDIAN);
						if (cid == -1) {
							// null, do nothing
						} else if (cid < 0) {
							// negative cid means value is
							// embedded
							// in record
							EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass((short) (-cid));
							if (referenceClass == null) {
								throw new BimserverDatabaseException("No class found for cid " + (-cid));
							}
							EStructuralFeature wv = referenceClass.getEStructuralFeature("wrappedValue");
							if (wv != null && wv.isMany()) {
								HashMapVirtualObject eObject = new HashMapVirtualObject(reusable, referenceClass);
								readList(eObject, buffer, wv);
								idEObject.setListItem(feature, i, eObject);
							} else {
								idEObject.setListItem(feature, i, readWrappedValue(feature, buffer, referenceClass));
							}
							idEObject.addUseForSerialization(feature, i);
						} else if (cid > 0) {
							// positive cid means value is a
							// reference
							// to another record
							EClass referenceClass = queryObjectProvider.getDatabaseSession().getEClass(cid);
							if (referenceClass == null) {
								throw new BimserverDatabaseException("Cannot find class with cid " + cid);
							}
							buffer.position(buffer.position() - 2);
							long rf = readReference(buffer, feature, referenceClass);
							idEObject.setListItemReference(feature, i, referenceClass, rf, -1);
							if (rf != -1) {
								if (queryObjectProvider.hasReadOrIsGoingToRead((rf)) || queryObjectProvider.hasReadOrIsGoingToRead(referenceClass)) {
									idEObject.addUseForSerialization(feature, i);
								}
							}
//						} else if (cid == 0) {
//							throw new BimserverDatabaseException("Cid cannot be 0");
						}
					}
				}
			}
		} else if (feature.getEType() instanceof EDataType) {
			int listSize = buffer.getInt();
			if (listSize < 0) {
				throw new BimserverDatabaseException("Negative array size for " + feature.getEContainingClass().getName() + "." + feature.getName());
			}
			for (int i = 0; i < listSize; i++) {
				Object reference = readPrimitiveValue(feature.getEType(), buffer);
				if (reference != null) {
					idEObject.setListItem(feature, i, reference);
				}
			}
		}
		return null;
	}
	
	public HashMapVirtualObject getByOid(long oid) throws BimserverDatabaseException {
		return getByOid(oid, false);
	}
	
	public HashMapVirtualObject getByOid(long oid, boolean useCache) throws BimserverDatabaseException {
		HashMapVirtualObject byOid = getQueryObjectProvider().getFromCache((long)oid);
		if (byOid != null) {
			return byOid;
		}
		EClass eClass = getQueryObjectProvider().getDatabaseSession().getEClassForOid(oid);
		ByteBuffer mustStartWith = ByteBuffer.wrap(new byte[12]);
		mustStartWith.putInt(reusable.getPid());
		mustStartWith.putLong(oid);
		ByteBuffer startSearchWith = ByteBuffer.wrap(new byte[16]);
		startSearchWith.putInt(reusable.getPid());
		startSearchWith.putLong(oid);
		startSearchWith.putInt(-reusable.getRid());
	
		SearchingRecordIterator recordIterator = getQueryObjectProvider().getDatabaseSession().getKeyValueStore().getRecordIterator(eClass.getEPackage().getName() + "_" + eClass.getName(), mustStartWith.array(),
				startSearchWith.array(), getQueryObjectProvider().getDatabaseSession());
		try {
			Record record = recordIterator.next();
			if (record == null) {
				return null;
			}
			getQueryObjectProvider().incReads();
			ByteBuffer keyBuffer = ByteBuffer.wrap(record.getKey());
			ByteBuffer valueBuffer = ByteBuffer.wrap(record.getValue());
			keyBuffer.getInt(); // pid
			long keyOid = keyBuffer.getLong();
			int keyRid = -keyBuffer.getInt();
			if (keyRid <= reusable.getRid()) {
				if (valueBuffer.capacity() == 1 && valueBuffer.get(0) == -1) {
					valueBuffer.position(valueBuffer.position() + 1);
					return null;
					// deleted entity
				} else {
					byOid = convertByteArrayToObject(eClass, keyOid, valueBuffer, keyRid);
					if (byOid != null && useCache) {
						getQueryObjectProvider().cache(byOid);
					}
					return byOid;
				}
			} else {
				return null;
			}
		} finally {
			recordIterator.close();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy