package org.bimserver.ifc.step.serializer; /****************************************************************************** * 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.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import org.bimserver.BimserverDatabaseException; import org.bimserver.emf.PackageMetaData; import org.bimserver.ifc.step.deserializer.IfcParserWriterUtils; import org.bimserver.models.geometry.GeometryPackage; import org.bimserver.models.store.IfcHeader; import org.bimserver.plugins.PluginConfiguration; import org.bimserver.plugins.PluginManagerInterface; import org.bimserver.plugins.serializers.ObjectProvider; import org.bimserver.plugins.serializers.OidConvertingSerializer; import org.bimserver.plugins.serializers.ProjectInfo; import org.bimserver.plugins.serializers.SerializerException; import org.bimserver.plugins.serializers.SerializerInputstream; import org.bimserver.plugins.serializers.StreamingReader; import org.bimserver.plugins.serializers.StreamingSerializer; import org.bimserver.shared.AbstractHashMapVirtualObject; import org.bimserver.shared.HashMapVirtualObject; import org.bimserver.shared.HashMapWrappedVirtualObject; import org.bimserver.shared.MinimalVirtualObject; import org.bimserver.utils.StringUtils; import org.bimserver.utils.UTF8PrintWriter; import org.eclipse.emf.common.util.EList; 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.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; import nl.tue.buildingsmart.schema.EntityDefinition; import nl.tue.buildingsmart.schema.SchemaDefinition; public abstract class IfcStepStreamingSerializer implements StreamingSerializer, StreamingReader, OidConvertingSerializer { private static final EcorePackage ECORE_PACKAGE_INSTANCE = EcorePackage.eINSTANCE; private static final String NULL = "NULL"; private static final String OPEN_CLOSE_PAREN = "()"; private static final String ASTERISK = "*"; private static final String PAREN_CLOSE_SEMICOLON = ");"; private static final String DASH = "#"; private static final String IFC_LOGICAL = "IfcLogical"; private static final String IFC_BOOLEAN = "IfcBoolean"; private static final String DOT = "."; private static final String COMMA = ","; private static final String OPEN_PAREN = "("; private static final String CLOSE_PAREN = ")"; private static final String BOOLEAN_UNDEFINED = ".U."; private static final String DOLLAR = "$"; private static final String WRAPPED_VALUE = "wrappedValue"; private String headerSchema; private ObjectProvider objectProvider; private final Map<Long, Long> oidToEid = new Long2LongOpenHashMap(); private long oidCounter = 1; protected static enum Mode { HEADER, BODY, FOOTER, FINISHED } private Mode mode = Mode.HEADER; private IfcHeader ifcHeader; private PackageMetaData packageMetaData; private PrintWriter printWriter; @Override public boolean write(OutputStream outputStream) throws SerializerException, BimserverDatabaseException { if (this.printWriter == null) { this.printWriter = new UTF8PrintWriter(outputStream); } boolean result = false; try { result = processMode(); } catch (IOException e) { throw new SerializerException(e); } return result; } public Map<Long, Long> getOidToEid() { return oidToEid; } public Mode getMode() { return mode; } public void setMode(Mode mode) { this.mode = mode; } @Override public InputStream getInputStream() { return new SerializerInputstream(this); } public IfcStepStreamingSerializer(PluginConfiguration pluginConfiguration) { } protected void setHeaderSchema(String headerSchema) { this.headerSchema = headerSchema; } @Override public void init(ObjectProvider objectProvider, ProjectInfo projectInfo, IfcHeader ifcHeader, PluginManagerInterface pluginManager, PackageMetaData packageMetaData) throws SerializerException { this.objectProvider = objectProvider; this.ifcHeader = ifcHeader; this.packageMetaData = packageMetaData; } public void writeToOutputStream(OutputStream outputStream) throws SerializerException, BimserverDatabaseException { this.printWriter = new UTF8PrintWriter(outputStream); try { while (mode != Mode.FINISHED) { processMode(); } } catch (Exception e) { throw new SerializerException(e); } } private boolean processMode() throws IOException, BimserverDatabaseException, SerializerException { if (getMode() == Mode.HEADER) { writeHeader(); setMode(Mode.BODY); } else if (getMode() == Mode.BODY) { HashMapVirtualObject next = objectProvider.next(); if (next != null) { write(next); } else { setMode(Mode.FOOTER); } } else if (getMode() == Mode.FOOTER) { writeFooter(); setMode(Mode.FINISHED); if (printWriter != null) { printWriter.flush(); } } else if (getMode() == Mode.FINISHED) { return false; } return true; } private void writeFooter() throws IOException { println("ENDSEC;"); println("END-ISO-10303-21;"); } private void writeHeader() throws IOException { SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); println("ISO-10303-21;"); println("HEADER;"); if (ifcHeader == null) { Date date = new Date(); println("FILE_DESCRIPTION ((''), '2;1');"); println("FILE_NAME ('', '" + dateFormatter.format(date) + "', (''), (''), '', 'BIMserver', '');"); println("FILE_SCHEMA (('" + headerSchema + "'));"); } else { print("FILE_DESCRIPTION (("); print(StringUtils.concat(ifcHeader.getDescription(), "'", ", ")); println("), '" + ifcHeader.getImplementationLevel() + "');"); println("FILE_NAME ('" + ifcHeader.getFilename().replace("\\", "\\\\") + "', '" + dateFormatter.format(ifcHeader.getTimeStamp()) + "', (" + StringUtils.concat(ifcHeader.getAuthor(), "'", ", ") + "), (" + StringUtils.concat(ifcHeader.getOrganization(), "'", ", ") + "), '" + ifcHeader.getPreProcessorVersion() + "', '" + ifcHeader.getOriginatingSystem() + "', '" + ifcHeader.getAuthorization() + "');"); // println("FILE_SCHEMA (('" + ifcHeader.getIfcSchemaVersion() + "'));"); println("FILE_SCHEMA (('" + headerSchema + "'));"); } println("ENDSEC;"); println("DATA;"); // println("//This program comes with ABSOLUTELY NO WARRANTY."); // println("//This is free software, and you are welcome to redistribute it under certain conditions. See www.bimserver.org <http://www.bimserver.org>"); } private void println(String line) throws IOException { printWriter.println(line); } private void print(String text) throws IOException { printWriter.write(text); } private void write(HashMapVirtualObject object) throws SerializerException, IOException { EClass eClass = object.eClass(); if (eClass.getEPackage() == GeometryPackage.eINSTANCE) { return; } if (eClass.getEAnnotation("hidden") != null) { return; } print(DASH); long convertedKey = getExpressId(object); if (convertedKey == -1) { throw new SerializerException("Going to serialize an object with id -1 (" + object.eClass().getName() + ")"); } print(String.valueOf(convertedKey)); print("= "); String upperCase = packageMetaData.getUpperCase(eClass); if (upperCase == null) { throw new SerializerException("Type not found: " + eClass.getName()); } print(upperCase); print(OPEN_PAREN); boolean isFirst = true; EntityDefinition entityBN = getSchemaDefinition().getEntityBN(object.eClass().getName()); for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) { if (feature.getEAnnotation("hidden") == null && (entityBN != null && (!entityBN.isDerived(feature.getName()) || entityBN.isDerivedOverride(feature.getName())))) { EClassifier type = feature.getEType(); if (type instanceof EEnum) { if (!isFirst) { print(COMMA); } writeEnum(object, feature); isFirst = false; } else if (type instanceof EClass) { EReference eReference = (EReference)feature; if (!packageMetaData.isInverse(eReference)) { if (!isFirst) { print(COMMA); } writeEClass(object, feature); isFirst = false; } } else if (type instanceof EDataType) { if (!isFirst) { print(COMMA); } writeEDataType(object, entityBN, feature); isFirst = false; } } } println(PAREN_CLOSE_SEMICOLON); } private long getExpressId(HashMapVirtualObject object) { return getExpressId(object.getOid()); } private long getExpressId(long oid) { if (oidToEid.containsKey(oid)) { return oidToEid.get(oid); } else { long eid = oidCounter++; oidToEid.put(oid, eid); return eid; } } private void writeEDataType(HashMapVirtualObject object, EntityDefinition entityBN, EStructuralFeature feature) throws SerializerException, IOException { if (entityBN != null && entityBN.isDerived(feature.getName())) { print(ASTERISK); } else if (feature.isMany()) { writeList(object, feature, false); } else { writeObject(object, feature); } } private void writeEClass(HashMapVirtualObject object, EStructuralFeature feature) throws SerializerException, IOException { Object referencedObject = object.eGet(feature); if (referencedObject instanceof AbstractHashMapVirtualObject && ((AbstractHashMapVirtualObject)referencedObject).eClass().getEAnnotation("wrapped") != null) { writeWrappedValue(object, feature, ((AbstractHashMapVirtualObject)referencedObject).eClass()); } else { if (referencedObject instanceof Long) { if (object.useFeatureForSerialization(feature)) { print(DASH); print(String.valueOf(getExpressId((Long) referencedObject))); } else { print(DOLLAR); } } else { EntityDefinition entityBN = getSchemaDefinition().getEntityBN(object.eClass().getName()); if (entityBN != null && entityBN.isDerived(feature.getName())) { print(ASTERISK); } else if (feature.isMany()) { writeList(object, feature, false); } else { writeObject(object, feature); } } } } private void writeObject(HashMapVirtualObject object, EStructuralFeature feature) throws SerializerException, IOException { Object ref = object.eGet(feature); if (ref == null || (feature.isUnsettable() && !object.eIsSet(feature))) { EClassifier type = feature.getEType(); if (type instanceof EClass) { EStructuralFeature structuralFeature = ((EClass) type).getEStructuralFeature(WRAPPED_VALUE); if (structuralFeature != null) { String name = structuralFeature.getEType().getName(); if (name.equals(IFC_BOOLEAN) || name.equals(IFC_LOGICAL) || structuralFeature.getEType() == EcorePackage.eINSTANCE.getEBoolean()) { print(BOOLEAN_UNDEFINED); } else { print(DOLLAR); } } else { print(DOLLAR); } } else { if (type == EcorePackage.eINSTANCE.getEBoolean()) { print(BOOLEAN_UNDEFINED); } else if (feature.isMany()) { print("()"); } else { print(DOLLAR); } } } else { if (ref instanceof HashMapWrappedVirtualObject) { writeEmbedded((HashMapWrappedVirtualObject) ref); } else if (feature.getEType() == ECORE_PACKAGE_INSTANCE.getEDouble()) { EStructuralFeature asStringFeature = object.eClass().getEStructuralFeature(feature.getName() + "AsString"); String asString = (String) object.eGet(asStringFeature); writeDoubleValue((Double)ref, asString, feature); } else { IfcParserWriterUtils.writePrimitive(ref, printWriter); } } } private void writeDoubleValue(double value, String asString, EStructuralFeature feature) throws SerializerException, IOException { if (asString != null) { print((String)asString); return; } IfcParserWriterUtils.writePrimitive(value, printWriter); } private void writeEmbedded(HashMapWrappedVirtualObject eObject) throws SerializerException, IOException { EClass class1 = eObject.eClass(); print(packageMetaData.getUpperCase(class1)); print(OPEN_PAREN); EStructuralFeature structuralFeature = class1.getEStructuralFeature(WRAPPED_VALUE); if (structuralFeature != null) { Object realVal = eObject.eGet(structuralFeature); if (structuralFeature.getEType() == ECORE_PACKAGE_INSTANCE.getEDouble()) { EStructuralFeature asStringFeature = eObject.eClass().getEStructuralFeature(structuralFeature.getName() + "AsString"); String asString = (String) eObject.eGet(asStringFeature); writeDoubleValue((Double)realVal, asString, structuralFeature); } else { IfcParserWriterUtils.writePrimitive(realVal, printWriter); } } print(CLOSE_PAREN); } /* * Use force when you want to force serialization (usually when serializing the second child of twodimensionalarray) */ private void writeList(MinimalVirtualObject object, EStructuralFeature feature, boolean force) throws SerializerException, IOException { List<?> list = (List<?>) object.eGet(feature); if (list == null) { if (feature.isUnsettable()) { print(DOLLAR); } else { print(OPEN_CLOSE_PAREN); } return; } List<?> doubleStingList = null; if (feature.getEType() == EcorePackage.eINSTANCE.getEDouble()) { EStructuralFeature doubleStringFeature = feature.getEContainingClass().getEStructuralFeature(feature.getName() + "AsString"); if (doubleStringFeature == null) { throw new SerializerException("Field " + feature.getName() + "AsString" + " not found"); } doubleStingList = (List<?>) object.eGet(doubleStringFeature); } if (list.isEmpty() || (!object.useFeatureForSerialization(feature) && feature.getEAnnotation("twodimensionalarray") == null && !force)) { if (!feature.isUnsettable()) { print(OPEN_CLOSE_PAREN); } else { print("$"); } } else { print(OPEN_PAREN); boolean first = true; int index = 0; for (Object listObject : list) { if (object.useFeatureForSerialization(feature, index) || (feature.getEAnnotation("twodimensionalarray") != null || force)) { if (!first) { print(COMMA); } if (feature instanceof EReference && listObject instanceof Long) { print(DASH); print(String.valueOf(getExpressId((Long)listObject))); } else { if (listObject == null) { print(DOLLAR); } else { if (listObject instanceof HashMapWrappedVirtualObject && feature.getEType().getEAnnotation("wrapped") != null) { HashMapWrappedVirtualObject eObject = (HashMapWrappedVirtualObject) listObject; Object realVal = eObject.eGet(eObject.eClass().getEStructuralFeature("wrappedValue")); if (realVal instanceof Double) { Object stringVal = eObject.eGet(eObject.eClass().getEStructuralFeature("wrappedValueAsString")); if (stringVal != null) { print((String) stringVal); } else { IfcParserWriterUtils.writePrimitive(realVal, printWriter); } } else { IfcParserWriterUtils.writePrimitive(realVal, printWriter); } } else if (listObject instanceof HashMapVirtualObject && feature.getEAnnotation("twodimensionalarray") != null) { HashMapVirtualObject td = (HashMapVirtualObject)listObject; writeList(td, td.eClass().getEStructuralFeature("List"), true); } else if (listObject instanceof HashMapVirtualObject) { HashMapVirtualObject td = (HashMapVirtualObject)listObject; print(packageMetaData.getUpperCase(td.eClass())); print(OPEN_PAREN); writeList(td, td.eClass().getEStructuralFeature("wrappedValue"), false); print(CLOSE_PAREN); } else if (listObject instanceof HashMapWrappedVirtualObject) { HashMapWrappedVirtualObject eObject = (HashMapWrappedVirtualObject) listObject; EClass class1 = eObject.eClass(); EStructuralFeature structuralFeature = class1.getEStructuralFeature(WRAPPED_VALUE); if (structuralFeature != null) { Object realVal = eObject.eGet(structuralFeature); print(packageMetaData.getUpperCase(class1)); print(OPEN_PAREN); if (realVal instanceof Double) { EStructuralFeature asStringFeature = eObject.eClass().getEStructuralFeature(structuralFeature.getName() + "AsString"); String asString = (String) eObject.eGet(asStringFeature); writeDoubleValue((Double)realVal, asString, structuralFeature); } else { IfcParserWriterUtils.writePrimitive(realVal, printWriter); } print(CLOSE_PAREN); } else { if (feature.getEAnnotation("twodimensionalarray") != null) { writeList(eObject, eObject.eClass().getEStructuralFeature("List"), true); } else { // LOGGER.info("Unfollowable reference found from " + object + "(" + object.getOid() + ")." + feature.getName() + " to " + eObject + "(" + eObject.getOid() + ")"); } } } else { if (doubleStingList != null) { if (index < doubleStingList.size()) { String val = (String)doubleStingList.get(index); if (val == null) { IfcParserWriterUtils.writePrimitive(listObject, printWriter); } else { print(val); } } else { IfcParserWriterUtils.writePrimitive(listObject, printWriter); } } else { IfcParserWriterUtils.writePrimitive(listObject, printWriter); } } } } first = false; } index++; } print(CLOSE_PAREN); } } private void writeWrappedValue(HashMapVirtualObject object, EStructuralFeature feature, EClass ec) throws SerializerException, IOException { Object get = object.eGet(feature); boolean isWrapped = ec.getEAnnotation("wrapped") != null; EStructuralFeature structuralFeature = ec.getEStructuralFeature(WRAPPED_VALUE); if (get instanceof HashMapWrappedVirtualObject) { boolean isDefinedWrapped = feature.getEType().getEAnnotation("wrapped") != null; HashMapWrappedVirtualObject betweenObject = (HashMapWrappedVirtualObject) get; if (betweenObject != null) { if (isWrapped && isDefinedWrapped) { Object val = betweenObject.eGet(structuralFeature); String name = structuralFeature.getEType().getName(); if ((name.equals(IFC_BOOLEAN) || name.equals(IFC_LOGICAL)) && val == null) { print(BOOLEAN_UNDEFINED); } else if (structuralFeature.getEType() == ECORE_PACKAGE_INSTANCE.getEDouble()) { EStructuralFeature asStringFeature = betweenObject.eClass().getEStructuralFeature(feature.getName() + "AsString"); String asString = (String) betweenObject.eGet(asStringFeature); writeDoubleValue((Double)val, asString, feature); } else { IfcParserWriterUtils.writePrimitive(val, printWriter); } } else { writeEmbedded(betweenObject); } } } else if (get instanceof EList<?>) { EList<?> list = (EList<?>) get; if (list.isEmpty()) { if (!feature.isUnsettable()) { print(OPEN_CLOSE_PAREN); } else { print("$"); } } else { print(OPEN_PAREN); boolean first = true; for (Object o : list) { if (!first) { print(COMMA); } HashMapVirtualObject object2 = (HashMapVirtualObject) o; Object val = object2.eGet(structuralFeature); if (structuralFeature.getEType() == ECORE_PACKAGE_INSTANCE.getEDouble()) { EStructuralFeature asStringFeature = object2.eClass().getEStructuralFeature(feature.getName() + "AsString"); String asString = (String) object2.eGet(asStringFeature); writeDoubleValue((Double)val, asString, structuralFeature); } else { IfcParserWriterUtils.writePrimitive(val, printWriter); } first = false; } print(CLOSE_PAREN); } } else if (get == null) { EClassifier type = structuralFeature.getEType(); if (type.getName().equals("IfcBoolean") || type.getName().equals("IfcLogical") || type == ECORE_PACKAGE_INSTANCE.getEBoolean()) { print(BOOLEAN_UNDEFINED); } else { EntityDefinition entityBN = getSchemaDefinition().getEntityBN(object.eClass().getName()); if (entityBN != null && entityBN.isDerived(feature.getName())) { print(ASTERISK); } else { print(DOLLAR); } } } else { System.out.println("Unimplemented?"); } } private SchemaDefinition getSchemaDefinition() { return packageMetaData.getSchemaDefinition(); } private void writeEnum(HashMapVirtualObject object, EStructuralFeature feature) throws SerializerException, IOException { Object val = object.eGet(feature); if (feature.getEType().getName().equals("Tristate")) { if (val == null) { print(DOLLAR); } else { IfcParserWriterUtils.writePrimitive(val, printWriter); } } else { if (val == null) { print(DOLLAR); } else { if (((Enum<?>) val).toString().equals(NULL)) { print(DOLLAR); } else { print(DOT); print(val.toString()); print(DOT); } } } } }