org.bimserver.ifc.IfcModel.java Source code

Java tutorial

Introduction

Here is the source code for org.bimserver.ifc.IfcModel.java

Source

package org.bimserver.ifc;

/******************************************************************************
 * 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<http://www.gnu.org/licenses/>}.
 *****************************************************************************/

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.LinkedBlockingQueue;

import org.bimserver.emf.IdEObject;
import org.bimserver.emf.IdEObjectImpl;
import org.bimserver.emf.IdEObjectImpl.State;
import org.bimserver.emf.IfcModelInterface;
import org.bimserver.emf.IfcModelInterfaceException;
import org.bimserver.emf.ModelMetaData;
import org.bimserver.emf.OidProvider;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.models.ifc2x3tc1.IfcAnnotation;
import org.bimserver.models.ifc2x3tc1.IfcAnnotationCurveOccurrence;
import org.bimserver.models.ifc2x3tc1.IfcDimensionCurve;
import org.bimserver.models.ifc2x3tc1.IfcElement;
import org.bimserver.models.ifc2x3tc1.IfcGrid;
import org.bimserver.models.ifc2x3tc1.IfcLayeredItem;
import org.bimserver.models.ifc2x3tc1.IfcObjectDefinition;
import org.bimserver.models.ifc2x3tc1.IfcPresentationLayerAssignment;
import org.bimserver.models.ifc2x3tc1.IfcProduct;
import org.bimserver.models.ifc2x3tc1.IfcProductDefinitionShape;
import org.bimserver.models.ifc2x3tc1.IfcProductRepresentation;
import org.bimserver.models.ifc2x3tc1.IfcPropertyDefinition;
import org.bimserver.models.ifc2x3tc1.IfcRelAssociates;
import org.bimserver.models.ifc2x3tc1.IfcRelConnectsStructuralActivity;
import org.bimserver.models.ifc2x3tc1.IfcRelContainedInSpatialStructure;
import org.bimserver.models.ifc2x3tc1.IfcRelReferencedInSpatialStructure;
import org.bimserver.models.ifc2x3tc1.IfcRepresentation;
import org.bimserver.models.ifc2x3tc1.IfcRepresentationItem;
import org.bimserver.models.ifc2x3tc1.IfcRoot;
import org.bimserver.models.ifc2x3tc1.IfcStructuralActivityAssignmentSelect;
import org.bimserver.models.ifc2x3tc1.IfcStructuralItem;
import org.bimserver.models.ifc2x3tc1.IfcTerminatorSymbol;
import org.bimserver.plugins.objectidms.ObjectIDM;
import org.bimserver.shared.exceptions.PublicInterfaceNotFoundException;
import org.bimserver.shared.exceptions.ServerException;
import org.bimserver.shared.exceptions.UserException;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public abstract class IfcModel implements IfcModelInterface {

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(IfcModel.class);
    private final ModelMetaData modelMetaData = new ModelMetaData();
    private final Set<IfcModelChangeListener> changeListeners = new LinkedHashSet<IfcModelChangeListener>();

    // Object with oid
    private BiMap<Long, IdEObject> objects;

    // Objects without oid, usually embedded when serialized
    private final Set<IdEObject> unidentifiedObjects = new HashSet<IdEObject>();

    private Map<String, IfcRoot> guidIndexed;
    private Map<EClass, List<? extends IdEObject>> indexPerClass;
    private Map<EClass, List<? extends IdEObject>> indexPerClassWithSubTypes;
    private Map<EClass, Map<String, IdEObject>> guidIndex;
    private Map<EClass, Map<String, IdEObject>> nameIndex;
    private long oidCounter = 1;
    private boolean useDoubleStrings = true;
    private PackageMetaData packageMetaData;
    private Map<Integer, Long> pidRoidMap;

    public IfcModel(PackageMetaData packageMetaData, Map<Integer, Long> pidRoidMap, int size) {
        this.pidRoidMap = pidRoidMap;
        if (packageMetaData == null) {
            throw new IllegalArgumentException();
        }
        this.packageMetaData = packageMetaData;
        this.objects = HashBiMap.create(size);
    }

    public IfcModel(PackageMetaData packageMetaData, Map<Integer, Long> pidRoidMap) {
        this(packageMetaData, pidRoidMap, 16);
    }

    @SuppressWarnings("unchecked")
    private void buildIndex() {
        indexPerClass = new HashMap<EClass, List<? extends IdEObject>>();
        for (Long key : objects.keySet()) {
            IdEObject value = objects.get((Long) key);
            if (value != null) {
                List<? extends IdEObject> list = indexPerClass.get(value.eClass());
                if (list == null) {
                    list = new ArrayList<IdEObject>();
                    indexPerClass.put(value.eClass(), list);
                }
                ((List<IdEObject>) list).add(value);
            }
        }
    }

    public void rebuildIndexPerClass(EClass eClass) {
        if (indexPerClass == null) {
            indexPerClass = new HashMap<EClass, List<? extends IdEObject>>();
        }
        ArrayList<IdEObject> list = new ArrayList<IdEObject>();
        indexPerClass.put((EClass) eClass, list);
        for (Long key : objects.keySet()) {
            IdEObject value = objects.get((Long) key);
            if (eClass.isInstance(value)) {
                list.add(value);
            }
        }
    }

    private void buildIndexWithSubTypes() {
        indexPerClassWithSubTypes = new HashMap<EClass, List<? extends IdEObject>>();
        for (Long key : objects.keySet()) {
            IdEObject idEObject = objects.get(key);
            if (idEObject != null) {
                buildIndexWithSuperTypes(idEObject, idEObject.eClass());
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void buildIndexWithSuperTypes(IdEObject eObject, EClass eClass) {
        if (!indexPerClassWithSubTypes.containsKey(eClass)) {
            indexPerClassWithSubTypes.put(eClass, new ArrayList<IdEObject>());
        }
        ((List<IdEObject>) indexPerClassWithSubTypes.get(eClass)).add(eObject);
        for (EClass superClass : eClass.getESuperTypes()) {
            buildIndexWithSuperTypes(eObject, superClass);
        }
    }

    public void buildGuidIndex() {
        guidIndex = new HashMap<EClass, Map<String, IdEObject>>();
        if (objects.isEmpty()) {
            return;
        }
        for (EClassifier classifier : objects.values().iterator().next().eClass().getEPackage().getEClassifiers()) {
            if (classifier instanceof EClass) {
                Map<String, IdEObject> map = new TreeMap<String, IdEObject>();
                guidIndex.put((EClass) classifier, map);
            }
        }
        for (Long key : objects.keySet()) {
            IdEObject value = objects.get((Long) key);
            if (value instanceof IfcRoot) {
                IfcRoot ifcRoot = (IfcRoot) value;
                guidIndex.get(value.eClass()).put(ifcRoot.getGlobalId(), value);
            }
        }
    }

    public void buildNameIndex() {
        nameIndex = new HashMap<EClass, Map<String, IdEObject>>();
        for (EClassifier classifier : objects.values().iterator().next().eClass().getEPackage().getEClassifiers()) {
            if (classifier instanceof EClass) {
                Map<String, IdEObject> map = new TreeMap<String, IdEObject>();
                nameIndex.put((EClass) classifier, map);
            }
        }
        for (Long key : objects.keySet()) {
            IdEObject value = objects.get((Long) key);
            if (value instanceof IfcRoot) {
                IfcRoot ifcRoot = (IfcRoot) value;
                if (ifcRoot.getName() != null) {
                    nameIndex.get(value.eClass()).put(ifcRoot.getName(), value);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void sortAllAggregates(ObjectIDM objectIDM, IfcRoot ifcRoot) {
        for (EStructuralFeature eStructuralFeature : ifcRoot.eClass().getEAllStructuralFeatures()) {
            if (objectIDM.shouldFollowReference(ifcRoot.eClass(), ifcRoot.eClass(), eStructuralFeature)) {
                if (eStructuralFeature.getUpperBound() == -1 || eStructuralFeature.getUpperBound() > 1) {
                    if (eStructuralFeature.getEType() instanceof EClass) {
                        if (eStructuralFeature.getEType().getEAnnotation("wrapped") != null) {
                            EList<IdEObject> list = (EList<IdEObject>) ifcRoot.eGet(eStructuralFeature);
                            sortPrimitiveList(list);
                        } else {
                            EList<IdEObject> list = (EList<IdEObject>) ifcRoot.eGet(eStructuralFeature);
                            sortComplexList(objectIDM, ifcRoot.eClass(), list, eStructuralFeature);
                        }
                    }
                }
            }
        }
    }

    private void sortPrimitiveList(EList<IdEObject> list) {
        ECollections.sort(list, new Comparator<IdEObject>() {
            @Override
            public int compare(IdEObject o1, IdEObject o2) {
                return comparePrimitives(o1, o2);
            }
        });
    }

    private void sortComplexList(final ObjectIDM objectIDM, final EClass originalQueryClass, EList<IdEObject> list,
            EStructuralFeature eStructuralFeature) {
        final EClass type = (EClass) eStructuralFeature.getEType();
        ECollections.sort(list, new Comparator<IdEObject>() {
            @Override
            public int compare(IdEObject o1, IdEObject o2) {
                int i = 1;
                for (EStructuralFeature eStructuralFeature : type.getEAllStructuralFeatures()) {
                    if (objectIDM.shouldFollowReference(originalQueryClass, type, eStructuralFeature)) {
                        Object val1 = o1.eGet(eStructuralFeature);
                        Object val2 = o2.eGet(eStructuralFeature);
                        if (val1 != null && val2 != null) {
                            if (eStructuralFeature.getEType() instanceof EClass) {
                                if (eStructuralFeature.getEType().getEAnnotation("wrapped") != null) {
                                    int compare = comparePrimitives((IdEObject) val1, (IdEObject) val2);
                                    if (compare != 0) {
                                        return compare * i;
                                    }
                                }
                            }
                        }
                        i++;
                    }
                }
                return 0;
            }
        });
    }

    private int comparePrimitives(IdEObject o1, IdEObject o2) {
        EClass eClass = o1.eClass();
        EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature("wrappedValue");
        Object val1 = o1.eGet(eStructuralFeature);
        Object val2 = o2.eGet(eStructuralFeature);
        if (eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEString()) {
            return ((String) val1).compareTo((String) val2);
        } else if (eStructuralFeature.getEType() == EcorePackage.eINSTANCE.getEInt()) {
            return ((Integer) val1).compareTo((Integer) val2);
        } else {
            throw new RuntimeException("ni");
        }
    }

    @SuppressWarnings("unchecked")
    public <T extends IdEObject> List<T> getAll(EClass eClass) {
        if (indexPerClass == null) {
            buildIndex();
        }
        List<? extends IdEObject> list = indexPerClass.get(eClass);
        if (list == null) {
            return Collections.EMPTY_LIST;
        } else {
            return (List<T>) list;
        }
    }

    public <T extends IdEObject> List<T> getAll(Class<T> interfaceClass) {
        return getAll(packageMetaData.getEClassIncludingDependencies(interfaceClass));
    }

    @SuppressWarnings("unchecked")
    public <T extends IdEObject> List<T> getAllWithSubTypes(EClass eClass) {
        if (indexPerClassWithSubTypes == null) {
            buildIndexWithSubTypes();
        }
        List<? extends IdEObject> list = indexPerClassWithSubTypes.get(eClass);
        if (list == null) {
            return Collections.EMPTY_LIST;
        } else {
            return (List<T>) list;
        }
    }

    @Override
    public <T extends IdEObject> List<T> getAllWithSubTypes(Class<T> interfaceClass) {
        return getAllWithSubTypes(packageMetaData.getEClass(interfaceClass));
    }

    public Set<String> getGuids(EClass eClass) {
        if (guidIndex == null) {
            buildGuidIndex();
        }
        Map<String, IdEObject> map = guidIndex.get(eClass);
        if (map == null) {
            return new HashSet<String>();
        }
        return map.keySet();
    }

    public Set<String> getNames(EClass eClass) {
        if (nameIndex == null) {
            buildNameIndex();
        }
        return nameIndex.get(eClass).keySet();
    }

    public IdEObject getByName(EClass eClass, String name) {
        if (nameIndex == null) {
            buildNameIndex();
        }
        return nameIndex.get(eClass).get(name);
    }

    public long size() {
        return objects.size() + unidentifiedObjects.size();
    }

    public Set<Long> keySet() {
        return objects.keySet();
    }

    public IdEObject get(long oid) {
        return objects.get(oid);
    }

    public Collection<IdEObject> getValues() {
        return objects.values();
    }

    public Collection<IdEObject> getUnidentifiedValues() {
        return unidentifiedObjects;
    }

    public void add(long oid, IdEObject eObject) throws IfcModelInterfaceException {
        add(oid, eObject, false, false);
    }

    @Override
    public void addAllowMultiModel(long oid, IdEObject eObject) throws IfcModelInterfaceException {
        add(oid, eObject, false, true);
    }

    @SuppressWarnings("unchecked")
    private void add(long oid, IdEObject eObject, boolean ignoreDuplicateOids, boolean allowMultiModel)
            throws IfcModelInterfaceException {
        if (((IdEObjectImpl) eObject).hasModel() && !allowMultiModel
                && ((IdEObjectImpl) eObject).getModel() != this) {
            throw new IfcModelInterfaceException("This object (" + eObject + ") already belongs to a Model: "
                    + ((IdEObjectImpl) eObject).getModel() + ", not this " + this);
        }
        if (oid == -1 || eObject.eClass().getEAnnotation("wrapped") != null) {
            unidentifiedObjects.add(eObject);
        } else {
            if (objects.containsKey(oid)) {
                if (!ignoreDuplicateOids) {
                    if (objects.get(oid) != eObject) {
                        throw new IfcModelInterfaceException(
                                "Oid already stored: " + oid + " " + eObject + " (old: " + objects.get(oid));
                    }
                }
            } else {
                objects.put(oid, eObject);
                if (!((IdEObjectImpl) eObject).hasModel() || !allowMultiModel) {
                    ((IdEObjectImpl) eObject).setModel(this);
                }
                if (guidIndexed != null) {
                    indexGuid(eObject);
                }
                if (indexPerClassWithSubTypes != null) {
                    //               if (indexPerClassWithSubTypes.get(eObject.eClass()) != null) {
                    buildIndexWithSuperTypes(eObject, eObject.eClass());
                    //               }
                }
                if (indexPerClass != null) {
                    List<IdEObject> list = (List<IdEObject>) indexPerClass.get(eObject.eClass());
                    if (list == null) {
                        list = new ArrayList<>();
                        indexPerClass.put(eObject.eClass(), list);
                    }
                    list.add(eObject);
                }
            }
            for (IfcModelChangeListener ifcModelChangeListener : changeListeners) {
                ifcModelChangeListener.objectAdded(eObject);
            }
        }
    }

    public BiMap<Long, IdEObject> getObjects() {
        return (BiMap<Long, IdEObject>) objects;
    }

    public boolean contains(long oid) {
        return objects.containsKey(oid);
    }

    public boolean contains(IdEObject eObject) {
        return objects.containsValue(eObject);
    }

    public void indexGuids() {
        guidIndexed = new HashMap<String, IfcRoot>();
        for (IdEObject idEObject : objects.values()) {
            indexGuid(idEObject);
        }
    }

    private void indexGuid(IdEObject idEObject) {
        if (idEObject instanceof IfcRoot) {
            IfcRoot ifcRoot = (IfcRoot) idEObject;
            if (ifcRoot.getGlobalId() != null) {
                guidIndexed.put(ifcRoot.getGlobalId(), ifcRoot);
            }
        }
    }

    public boolean isValid() {
        return true;
    }

    public void dumpObject(IdEObject idEObject) {
        dumpObject(idEObject, 0, new HashSet<IdEObject>());
    }

    @SuppressWarnings("rawtypes")
    private void dumpObject(IdEObject idEObject, int indention, Set<IdEObject> printed) {
        if (printed.contains(idEObject)) {
            printIndention(indention);
            System.out.println("[REFERENCE: " + idEObject.getOid() + "]");
            return;
        }
        printed.add(idEObject);
        printIndention(indention);
        System.out.println(idEObject.eClass().getName() + " (" + idEObject.getOid() + ")");
        for (EAttribute eAttribute : idEObject.eClass().getEAllAttributes()) {
            Object val = idEObject.eGet(eAttribute);
            if (val != null) {
                printIndention(indention + 1);
                System.out.println(eAttribute.getName() + ": " + val);
            }
        }
        for (EReference eReference : idEObject.eClass().getEAllReferences()) {
            Object referencedObject = idEObject.eGet(eReference);
            if (eReference.isMany()) {
                List list = (List) referencedObject;
                if (list.size() > 0) {
                    printIndention(indention + 1);
                    System.out.println(eReference.getName() + ": ");
                    for (Object o : list) {
                        dumpObject((IdEObject) o, indention + 2, printed);
                    }
                }
            } else {
                if (referencedObject != null) {
                    printIndention(indention + 1);
                    System.out.println(eReference.getName() + ": ");
                    dumpObject((IdEObject) referencedObject, indention + 2, printed);
                }
            }
        }
    }

    private void printIndention(int indention) {
        for (int i = 0; i < indention; i++) {
            System.out.print("  ");
        }
    }

    public void dumpSummary() {
        Map<EClass, Integer> counts = new TreeMap<EClass, Integer>(new Comparator<EClass>() {
            @Override
            public int compare(EClass o1, EClass o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        for (IdEObject idEObject : objects.values()) {
            if (!counts.containsKey(idEObject.eClass())) {
                counts.put(idEObject.eClass(), 1);
            } else {
                counts.put(idEObject.eClass(), counts.get(idEObject.eClass()) + 1);
            }
        }
        for (EClass eClass : counts.keySet()) {
            System.out.println(eClass.getName() + ": " + counts.get(eClass));
        }
    }

    public void dump() {
        System.out.println("Dumping IFC Model");
        for (Long key : objects.keySet()) {
            System.out.println(key + ": " + objects.get(key).eClass().getName());
        }
    }

    public void dumpPlusReferences() {
        System.out.println("Dumping IFC Model + References");
        Set<IdEObject> done = new HashSet<IdEObject>();
        for (Long key : objects.keySet()) {
            dumpPlusReferences(done, objects.get(key));
        }
    }

    @SuppressWarnings("rawtypes")
    private void dumpPlusReferences(Set<IdEObject> done, IdEObject idEObject) {
        if (idEObject == null) {
            return;
        }
        if (done.contains(idEObject)) {
            return;
        }
        done.add(idEObject);
        System.out.println(idEObject.getOid() + ": " + idEObject.eClass().getName());
        for (EReference eReference : idEObject.eClass().getEAllReferences()) {
            Object val = idEObject.eGet(eReference);
            if (eReference.isMany()) {
                List list = (List) val;
                for (Object o : list) {
                    dumpPlusReferences(done, (IdEObject) o);
                }
            } else {
                dumpPlusReferences(done, (IdEObject) val);
            }
        }
    }

    public void remove(IdEObject idEObject) {
        unidentifiedObjects.remove(idEObject);
        objects.inverse().remove(idEObject);
        if (indexPerClass != null) {
            indexPerClass.get(idEObject.eClass()).remove(idEObject);
        }
        if (indexPerClassWithSubTypes != null) {
            indexPerClassWithSubTypes.get(idEObject.eClass()).remove(idEObject);
        }
    }

    public void setOid(IdEObject object, Long oid) {
        objects.forcePut(oid, object);
    }

    public void fixOids(OidProvider oidProvider) {
        BiMap<Long, IdEObject> temp = HashBiMap.create();
        for (long oid : objects.keySet()) {
            fixOids(objects.get(oid), oidProvider, temp);
        }
        objects = temp;
    }

    public void fixOidsFlat(OidProvider oidProvider) {
        BiMap<Long, IdEObject> temp = HashBiMap.create();
        for (long oid : objects.keySet()) {
            fixOidsFlat(objects.get(oid), oidProvider, temp);
        }
        objects = temp;
    }

    public void fixOids() {
        BiMap<Long, IdEObject> temp = HashBiMap.create();
        for (IdEObject object : objects.values()) {
            temp.put(object.getOid(), object);
        }
        objects = temp;
    }

    @SuppressWarnings("rawtypes")
    private void fixOids(IdEObject idEObject, OidProvider oidProvider, BiMap<Long, IdEObject> temp) {
        if (idEObject == null) {
            return;
        }
        if (temp.containsValue(idEObject)) {
            return;
        }
        ((IdEObjectImpl) idEObject).setOid(oidProvider.newOid(idEObject.eClass()));
        if (objects.containsValue(idEObject)) {
            temp.put(idEObject.getOid(), idEObject);
        }
        for (EReference eReference : idEObject.eClass().getEAllReferences()) {
            Object val = idEObject.eGet(eReference);
            if (eReference.isMany()) {
                List list = (List) val;
                for (Object o : list) {
                    fixOids((IdEObject) o, oidProvider, temp);
                }
            } else {
                fixOids((IdEObject) val, oidProvider, temp);
            }
        }
    }

    private void fixOidsFlat(IdEObject idEObject, OidProvider oidProvider, BiMap<Long, IdEObject> temp) {
        if (idEObject == null) {
            return;
        }
        if (temp.containsValue(idEObject)) {
            return;
        }
        ((IdEObjectImpl) idEObject).setOid(oidProvider.newOid(idEObject.eClass()));
        if (objects.containsValue(idEObject)) {
            temp.put(idEObject.getOid(), idEObject);
        }
    }

    public void setObjectOids() {
        for (long oid : objects.keySet()) {
            ((IdEObjectImpl) objects.get(oid)).setOid(oid);
        }
    }

    public long getHighestOid() {
        long max = 0;
        for (long oid : objects.keySet()) {
            if (oid > max) {
                max = oid;
            }
        }
        return max;
    }

    public void changeOid(IdEObject object) {
        objects.inverse().remove(object);
        objects.put(object.getOid(), object);
    }

    public IfcRoot getByGuid(String guid) {
        if (guidIndexed == null) {
            indexGuids();
        }
        return guidIndexed.get(guid);
    }

    public boolean containsGuid(String guid) {
        if (guidIndexed == null) {
            indexGuids();
        }
        return guidIndexed.containsKey(guid);
    }

    public void checkDoubleOids() {
        Set<Long> oids = new HashSet<Long>();
        for (IdEObject idEObject : objects.values()) {
            if (oids.contains(idEObject.getOid())) {
                throw new RuntimeException("Double oid: " + idEObject.getOid());
            }
            oids.add(idEObject.getOid());
        }
    }

    public void fixOidCounter() {
        oidCounter = getHighestOid() + 1;
    }

    @SuppressWarnings("rawtypes")
    private void checkDoubleOidsPlusReferences(BiMap<IdEObject, Long> done, IdEObject idEObject) {
        if (idEObject == null) {
            return;
        }
        if (idEObject.eClass().getEAnnotation("wrapped") != null) {
            return;
        }
        if (done.containsKey(idEObject)) {
            return;
        }
        if (done.containsValue(idEObject.getOid())) {
            showBackReferences(idEObject);
            IdEObject existing = done.inverse().get(idEObject.getOid());
            showBackReferences(existing);
            throw new RuntimeException("Double oid: " + idEObject.getOid() + " " + idEObject + ", " + existing);
        }
        done.put(idEObject, idEObject.getOid());
        for (EReference eReference : idEObject.eClass().getEAllReferences()) {
            if (eReference.isMany()) {
                List list = (List) idEObject.eGet(eReference);
                for (Object o : list) {
                    checkDoubleOidsPlusReferences(done, (IdEObject) o);
                }
            } else {
                checkDoubleOidsPlusReferences(done, (IdEObject) idEObject.eGet(eReference));
            }
        }
    }

    @SuppressWarnings("rawtypes")
    public void showBackReferences(IdEObject idEObject) {
        System.out.println("Showing back references to: " + idEObject);
        for (IdEObject object : getValues()) {
            for (EReference eReference : object.eClass().getEAllReferences()) {
                if (eReference.isMany()) {
                    List list = (List) object.eGet(eReference);
                    for (Object o : list) {
                        if (o == idEObject) {
                            System.out
                                    .println(object.eClass().getName() + "." + eReference.getName() + " " + object);
                        }
                    }
                } else {
                    Object o = object.eGet(eReference);
                    if (o == idEObject) {
                        System.out.println(object.eClass().getName() + "." + eReference.getName() + " " + object);
                    }
                }
            }
        }
    }

    public void checkDoubleOidsPlusReferences() {
        BiMap<IdEObject, Long> done = HashBiMap.create();
        for (IdEObject idEObject : objects.values()) {
            checkDoubleOidsPlusReferences(done, idEObject);
        }
    }

    public void resetOidsFlat() {
        for (IdEObject idEObject : objects.values()) {
            ((IdEObjectImpl) idEObject).setOid(-1);
        }
    }

    public void resetOids() {
        Set<IdEObject> done = new HashSet<IdEObject>();
        for (IdEObject idEObject : objects.values()) {
            resetOids(idEObject, done);
        }
    }

    @SuppressWarnings("rawtypes")
    public void resetOids(IdEObject idEObject, Set<IdEObject> done) {
        if (idEObject == null) {
            return;
        }
        if (done.contains(idEObject)) {
            return;
        }
        ((IdEObjectImpl) idEObject).setOid(-1);
        done.add(idEObject);
        for (EReference eReference : idEObject.eClass().getEAllReferences()) {
            Object val = idEObject.eGet(eReference);
            if (eReference.isMany()) {
                List list = (List) val;
                for (Object o : list) {
                    resetOids((IdEObject) o, done);
                }
            } else {
                resetOids((IdEObject) val, done);
            }
        }
    }

    public void addChangeListener(IfcModelChangeListener listener) {
        changeListeners.add(listener);
    }

    public void removeChangeListener(IfcModelChangeListener ifcModelChangeListener) {
        changeListeners.remove(ifcModelChangeListener);
    }

    @Override
    public void setUseDoubleStrings(boolean useDoubleStrings) {
        this.useDoubleStrings = useDoubleStrings;
    }

    public boolean isUseDoubleStrings() {
        return useDoubleStrings;
    }

    @Override
    public Iterator<IdEObject> iterator() {
        return objects.values().iterator();
    }

    @Override
    public int countWithSubtypes(EClass eClass) {
        List<IdEObject> list = getAllWithSubTypes(eClass);
        if (list == null) {
            return 0;
        }
        return list.size();
    }

    @Override
    public int count(EClass eClass) {
        List<IdEObject> list = getAll(eClass);
        if (list == null) {
            return 0;
        }
        return list.size();
    }

    public Iterator<IdEObject> iterateAllObjects() {
        return new Iterator<IdEObject>() {
            private final Queue<IdEObject> todo = new LinkedBlockingQueue<IdEObject>(getValues());
            private final Set<IdEObject> done = new HashSet<IdEObject>();

            @Override
            public boolean hasNext() {
                return !todo.isEmpty();
            }

            @SuppressWarnings("rawtypes")
            @Override
            public IdEObject next() {
                IdEObject idEObject = todo.poll();
                done.add(idEObject);
                for (EReference eReference : idEObject.eClass().getEAllReferences()) {
                    Object val = idEObject.eGet(eReference);
                    if (eReference.isMany()) {
                        List list = (List) val;
                        for (Object o : list) {
                            if (!done.contains(o)) {
                                todo.add((IdEObject) o);
                            }
                        }
                    } else {
                        if (val != null && !done.contains(val)) {
                            todo.add((IdEObject) val);
                        }
                    }
                }
                return idEObject;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    @Override
    public void generateMinimalExpressIds() {
        int expressId = 1;
        Iterator<IdEObject> iterateAllObjects = iterateAllObjects();
        while (iterateAllObjects.hasNext()) {
            IdEObject idEObject = iterateAllObjects.next();
            ((IdEObjectImpl) idEObject).setExpressId(expressId++);
        }
    }

    @Override
    public ModelMetaData getModelMetaData() {
        return modelMetaData;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T create(EClass eClass) throws IfcModelInterfaceException {
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        long oid = oidCounter++;
        ((IdEObjectImpl) object).setOid(oid);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T createAndAdd(Class<T> clazz) throws IfcModelInterfaceException {
        EClass eClass = packageMetaData.getEClass(clazz);
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        object.setLoadingState(State.LOADED);
        long oid = oidCounter++;
        add(oid, object);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T createAndAdd(EClass eClass) throws IfcModelInterfaceException {
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        object.setLoadingState(State.LOADED);
        long oid = oidCounter++;
        add(oid, object);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T create(EClass eClass, long oid) throws IfcModelInterfaceException {
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        object.setModel(this);
        object.setOid(oid);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T createAndAdd(EClass eClass, long oid) throws IfcModelInterfaceException {
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        object.setModel(this);
        object.setOid(oid);
        add(oid, object);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T create(EClass eClass, OidProvider oidProvider)
            throws IfcModelInterfaceException {
        IdEObjectImpl object = (IdEObjectImpl) eClass.getEPackage().getEFactoryInstance().create(eClass);
        long oid = oidProvider.newOid(eClass);
        ((IdEObjectImpl) object).setOid(oid);
        ((IdEObjectImpl) object).setLoadingState(State.LOADED);
        add(oid, object, false, false);
        return (T) object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T create(Class<T> clazz) throws IfcModelInterfaceException {
        return (T) create(packageMetaData.getEClassIncludingDependencies(clazz));
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IdEObject> T create(Class<T> clazz, OidProvider oidProvider)
            throws IfcModelInterfaceException {
        return (T) create(packageMetaData.getEClass(clazz), oidProvider);
    }

    @Override
    public void clear() {
        if (guidIndex != null) {
            guidIndex.clear();
        }
        if (guidIndexed != null) {
            guidIndexed.clear();
        }
        if (indexPerClass != null) {
            indexPerClass.clear();
        }
        if (nameIndex != null) {
            nameIndex.clear();
        }
        if (indexPerClassWithSubTypes != null) {
            indexPerClassWithSubTypes.clear();
        }
        if (objects != null) {
            objects.clear();
        }
    }

    @Override
    public void resetExpressIds() {
        for (IdEObject idEObject : objects.values()) {
            ((IdEObjectImpl) idEObject).setExpressId(-1);
        }
    }

    @Override
    public IfcModelInterface branch(long poid, boolean recordChanges) {
        throw new UnsupportedOperationException();
    }

    @Override
    public long commit(String comment) throws ServerException, UserException, PublicInterfaceNotFoundException {
        throw new UnsupportedOperationException();
    }

    @Override
    public PackageMetaData getPackageMetaData() {
        return packageMetaData;
    }

    @Override
    public void fixInverseMismatches() {
        int nrFixes = 0;
        for (IfcRelContainedInSpatialStructure ifcRelContainedInSpatialStructure : new ArrayList<>(
                getAll(IfcRelContainedInSpatialStructure.class))) {
            for (IfcProduct ifcProduct : new ArrayList<>(ifcRelContainedInSpatialStructure.getRelatedElements())) {
                if (ifcProduct instanceof IfcElement) {
                    IfcElement ifcElement = (IfcElement) ifcProduct;
                    ifcElement.getContainedInStructure().add(ifcRelContainedInSpatialStructure);
                    nrFixes++;
                } else if (ifcProduct instanceof IfcAnnotation) {
                    IfcAnnotation ifcAnnotation = (IfcAnnotation) ifcProduct;
                    ifcAnnotation.getContainedInStructure().add(ifcRelContainedInSpatialStructure);
                    nrFixes++;
                } else if (ifcProduct instanceof IfcGrid) {
                    IfcGrid ifcGrid = (IfcGrid) ifcProduct;
                    ifcGrid.getContainedInStructure().add(ifcRelContainedInSpatialStructure);
                    nrFixes++;
                }
            }
        }
        for (IfcPresentationLayerAssignment ifcPresentationLayerAssignment : new ArrayList<>(
                getAllWithSubTypes(IfcPresentationLayerAssignment.class))) {
            for (IfcLayeredItem ifcLayeredItem : new ArrayList<>(
                    ifcPresentationLayerAssignment.getAssignedItems())) {
                if (ifcLayeredItem instanceof IfcRepresentation) {
                    IfcRepresentation ifcRepresentation = (IfcRepresentation) ifcLayeredItem;
                    ifcRepresentation.getLayerAssignments().add(ifcPresentationLayerAssignment);
                    nrFixes++;
                } else if (ifcLayeredItem instanceof IfcRepresentationItem) {
                    IfcRepresentationItem ifcRepresentationItem = (IfcRepresentationItem) ifcLayeredItem;
                    ifcRepresentationItem.getLayerAssignments().add(ifcPresentationLayerAssignment);
                    nrFixes++;
                }
            }
        }
        for (IfcRelAssociates ifcRelAssociates : new ArrayList<>(getAllWithSubTypes(IfcRelAssociates.class))) {
            for (IfcRoot ifcRoot : new ArrayList<>(ifcRelAssociates.getRelatedObjects())) {
                if (ifcRoot instanceof IfcObjectDefinition) {
                    ((IfcObjectDefinition) ifcRoot).getHasAssociations().add(ifcRelAssociates);
                    nrFixes++;
                } else if (ifcRoot instanceof IfcPropertyDefinition) {
                    ((IfcPropertyDefinition) ifcRoot).getHasAssociations().add(ifcRelAssociates);
                    nrFixes++;
                }
            }
        }
        for (IfcTerminatorSymbol ifcTerminatorSymbol : new ArrayList<>(
                getAllWithSubTypes(IfcTerminatorSymbol.class))) {
            IfcAnnotationCurveOccurrence ifcAnnotationCurveOccurrence = ifcTerminatorSymbol.getAnnotatedCurve();
            if (ifcAnnotationCurveOccurrence instanceof IfcDimensionCurve) {
                ((IfcDimensionCurve) ifcAnnotationCurveOccurrence).getAnnotatedBySymbols().add(ifcTerminatorSymbol);
                nrFixes++;
            }
        }
        for (IfcRelReferencedInSpatialStructure ifcRelReferencedInSpatialStructure : new ArrayList<>(
                getAllWithSubTypes(IfcRelReferencedInSpatialStructure.class))) {
            for (IfcProduct ifcProduct : new ArrayList<>(ifcRelReferencedInSpatialStructure.getRelatedElements())) {
                if (ifcProduct instanceof IfcElement) {
                    ((IfcElement) ifcProduct).getReferencedInStructures().add(ifcRelReferencedInSpatialStructure);
                    nrFixes++;
                }
            }
        }
        for (IfcProduct ifcProduct : new ArrayList<>(getAllWithSubTypes(IfcProduct.class))) {
            IfcProductRepresentation ifcProductRepresentation = ifcProduct.getRepresentation();
            if (ifcProductRepresentation instanceof IfcProductDefinitionShape) {
                ((IfcProductDefinitionShape) ifcProductRepresentation).getShapeOfProduct().add(ifcProduct);
                nrFixes++;
            }
        }
        for (IfcRelConnectsStructuralActivity ifcRelConnectsStructuralActivity : new ArrayList<>(
                getAllWithSubTypes(IfcRelConnectsStructuralActivity.class))) {
            IfcStructuralActivityAssignmentSelect ifcStructuralActivityAssignmentSelect = ifcRelConnectsStructuralActivity
                    .getRelatingElement();
            if (ifcStructuralActivityAssignmentSelect instanceof IfcStructuralItem) {
                ((IfcStructuralItem) ifcStructuralActivityAssignmentSelect).getAssignedStructuralActivity()
                        .add(ifcRelConnectsStructuralActivity);
                nrFixes++;
            }
        }
        LOGGER.info("Nr inverse fixes: " + nrFixes);
    }

    @Override
    public Map<Integer, Long> getPidRoidMap() {
        return pidRoidMap;
    }

    @Override
    public void set(IdEObject idEObject, EStructuralFeature eFeature, Object newValue) {
    }

    @Override
    public void checkin(long poid, String comment)
            throws ServerException, UserException, PublicInterfaceNotFoundException {
    }

    @Override
    public boolean containsNoFetch(long oid) {
        return contains(oid);
    }

    @Override
    public IdEObject getNoFetch(long oid) {
        return get(oid);
    }

    public abstract void load(IdEObject idEObject);

    @Override
    public Set<EClass> getUsedClasses() {
        if (indexPerClass == null) {
            buildIndex();
        }
        return indexPerClass.keySet();
    }

    @Override
    public void query(ObjectNode query) {
    }
}