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<http://www.gnu.org/licenses/>}. *****************************************************************************/ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bimserver.BimserverDatabaseException; import org.bimserver.database.DatabaseSession; import org.bimserver.database.DatabaseSession.GetResult; import org.bimserver.database.ObjectIdentifier; import org.bimserver.database.Record; import org.bimserver.database.SearchingRecordIterator; import org.bimserver.database.queries.om.QueryException; import org.bimserver.database.queries.om.QueryPart; import org.bimserver.plugins.deserializers.DatabaseInterface; import org.bimserver.shared.HashMapVirtualObject; import org.bimserver.shared.QueryContext; import org.bimserver.utils.BinUtils; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EStructuralFeature; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.google.common.base.Charsets; public class QueryClassificationsAndTypesStackFrame extends DatabaseReadingStackFrame { private EClass eClass; private SearchingRecordIterator typeRecordIterator; private Record record; private Set<Long> allowedOids = new HashSet<>(); @SuppressWarnings("unchecked") public QueryClassificationsAndTypesStackFrame(QueryObjectProvider queryObjectProvider, EClass eClass, QueryPart partialQuery, QueryContext reusable, Set<String> classifications) throws BimserverDatabaseException { super(reusable, queryObjectProvider, partialQuery); this.eClass = eClass; DatabaseSession databaseSession = getQueryObjectProvider().getDatabaseSession(); String schemaName = eClass.getEPackage().getName(); EClass classificationReferenceClass = databaseSession.getEClass(schemaName, "IfcClassificationReference"); EClass relAssociatesClassificationReferenceClass = databaseSession.getEClass(schemaName, "IfcRelAssociatesClassification"); EStructuralFeature classificationKeyFeature = classificationReferenceClass.getEStructuralFeature(1); // renamed from "ItemReference" in IFC2x3 to "Identification" in IFC4 for (String classification : classifications) { List<ObjectIdentifier> objectIdentifiers = getOids(classificationReferenceClass, classificationKeyFeature, classification, databaseSession, reusable.getPid(), reusable.getRid()); for (ObjectIdentifier objectIdentifier : objectIdentifiers) { // Now we need to get all the IfcRelAssociatesClassification objects referencing this one List<ObjectIdentifier> relAssociates = getOids(relAssociatesClassificationReferenceClass, relAssociatesClassificationReferenceClass.getEStructuralFeature("RelatingClassification"), objectIdentifier.getOid(), databaseSession, reusable.getPid(), reusable.getRid()); for (ObjectIdentifier objectIdentifier2 : relAssociates) { HashMapVirtualObject relAssociatesClassification = getByOid(objectIdentifier2.getOid()); List<Long> relatedObjects = (List<Long>) relAssociatesClassification.eGet(relAssociatesClassificationReferenceClass.getEStructuralFeature("RelatedObjects")); allowedOids.addAll(relatedObjects); } } } String tableName = eClass.getEPackage().getName() + "_" + eClass.getName(); if (getReusable().getOidCounters() != null) { if (!getReusable().getOidCounters().containsKey(eClass)) { return; // will skip to next one } long startOid = getReusable().getOidCounters().get(eClass); ByteBuffer tmp = ByteBuffer.allocate(12); tmp.putInt(getReusable().getPid()); tmp.putLong(startOid + 1); typeRecordIterator = queryObjectProvider.getDatabaseSession().getKeyValueStore().getRecordIterator(tableName, BinUtils.intToByteArray(getReusable().getPid()), tmp.array(), queryObjectProvider.getDatabaseSession()); record = typeRecordIterator.next(); } else { // LOGGER.warn("Potential too-many-reads"); typeRecordIterator = queryObjectProvider.getDatabaseSession().getKeyValueStore().getRecordIterator(tableName, BinUtils.intToByteArray(getReusable().getPid()), BinUtils.intToByteArray(getReusable().getPid()), queryObjectProvider.getDatabaseSession()); record = typeRecordIterator.next(); } } public ObjectIdentifier getOid(EClass eClass, EAttribute attribute, Object value, DatabaseInterface databaseInterface, int pid, int rid) throws BimserverDatabaseException { if (attribute.getEAnnotation("singleindex") != null) { String indexTableName = attribute.getEContainingClass().getEPackage().getName() + "_" + eClass.getName() + "_" + attribute.getName(); byte[] queryBytes = null; if (value instanceof String) { queryBytes = ((String)value).getBytes(Charsets.UTF_8); } else if (value instanceof Integer) { queryBytes = BinUtils.intToByteArray((Integer)value); } else { throw new BimserverDatabaseException("Unsupported type " + value); } ByteBuffer valueBuffer = ByteBuffer.allocate(queryBytes.length + 8); valueBuffer.putInt(pid); valueBuffer.putInt(-rid); valueBuffer.put(queryBytes); byte[] firstDuplicate = databaseInterface.get(indexTableName, valueBuffer.array()); if (firstDuplicate != null) { ByteBuffer buffer = ByteBuffer.wrap(firstDuplicate); buffer.getInt(); // pid long oid = buffer.getLong(); return new ObjectIdentifier(oid, (short)oid); } } else { throw new UnsupportedOperationException(eClass.getName() + "." + attribute.getName() + " does not have a singleindex"); } return null; } public List<ObjectIdentifier> getOids(EClass eClass, EStructuralFeature eStructuralFeature, Object value, DatabaseInterface databaseInterface, int pid, int rid) throws BimserverDatabaseException { if (eStructuralFeature.getEAnnotation("singleindex") != null) { List<ObjectIdentifier> result = new ArrayList<>(); String indexTableName = eStructuralFeature.getEContainingClass().getEPackage().getName() + "_" + eClass.getName() + "_" + eStructuralFeature.getName(); byte[] queryBytes = null; if (value instanceof String) { queryBytes = ((String)value).getBytes(Charsets.UTF_8); } else if (value instanceof Integer) { queryBytes = BinUtils.intToByteArray((Integer)value); } else if (value instanceof Long) { queryBytes = BinUtils.longToByteArray((Long)value); } else { throw new BimserverDatabaseException("Unsupported type " + value); } ByteBuffer valueBuffer = ByteBuffer.allocate(queryBytes.length + 8); valueBuffer.putInt(pid); valueBuffer.putInt(-rid); valueBuffer.put(queryBytes); List<byte[]> duplicates = databaseInterface.getDuplicates(indexTableName, valueBuffer.array()); for (byte[] duplicate : duplicates) { ByteBuffer buffer = ByteBuffer.wrap(duplicate); buffer.getInt(); // pid long oid = buffer.getLong(); result.add(new ObjectIdentifier(oid, (short)oid)); } return result; } else { throw new UnsupportedOperationException(); } } @Override boolean process() throws BimserverDatabaseException, QueryException, JsonParseException, JsonMappingException, IOException { if (typeRecordIterator == null) { return true; } if (record == null) { currentObject = null; typeRecordIterator.close(); return true; } currentObject = null; ByteBuffer nextKeyStart = ByteBuffer.allocate(12); getQueryObjectProvider().incReads(); ByteBuffer keyBuffer = ByteBuffer.wrap(record.getKey()); int keyPid = keyBuffer.getInt(); long keyOid = keyBuffer.getLong(); int keyRid = -keyBuffer.getInt(); ByteBuffer valueBuffer = ByteBuffer.wrap(record.getValue()); GetResult map = getMap(eClass, eClass, valueBuffer, keyPid, keyOid, keyRid); if (map == GetResult.CONTINUE_WITH_NEXT_OID) { nextKeyStart.position(0); nextKeyStart.putInt(getReusable().getPid()); nextKeyStart.putLong(keyOid + 1); record = typeRecordIterator.next(nextKeyStart.array()); } else { record = typeRecordIterator.next(); } if (currentObject != null) { if (!allowedOids.contains(currentObject.getOid())) { currentObject = null; } } processPossibleIncludes(currentObject, eClass, getQueryPart()); return false; } }