package org.bimserver.ifc.xml.deserializer; /****************************************************************************** * Copyright (C) 2009-2014 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 <http://www.gnu.org/licenses/>. *****************************************************************************/ import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.bimserver.emf.IdEObject; import org.bimserver.emf.IfcModelInterface; import org.bimserver.emf.IfcModelInterfaceException; import org.bimserver.ifc.IfcModel; import org.bimserver.models.ifc2x3tc1.Ifc2x3tc1Factory; import org.bimserver.models.ifc2x3tc1.Ifc2x3tc1Package; import org.bimserver.plugins.deserializers.DeserializeException; import org.bimserver.plugins.deserializers.EmfDeserializer; import org.bimserver.plugins.schema.SchemaDefinition; import org.bimserver.utils.FakeClosingInputStream; 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.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; public class IfcXmlDeserializer extends EmfDeserializer { private final IfcModel model = new IfcModel(); @Override public IfcModelInterface read(InputStream inputStream, String filename, long fileSize) throws DeserializeException { if (filename != null && (filename.toUpperCase().endsWith(".ZIP") || filename.toUpperCase().endsWith(".IFCZIP") || filename.toUpperCase().endsWith(".IFCXMLZIP"))) { ZipInputStream zipInputStream = new ZipInputStream(inputStream); ZipEntry nextEntry; try { nextEntry = zipInputStream.getNextEntry(); if (nextEntry == null) { throw new DeserializeException("Zip files must contain exactly one IFC-file, this zip-file looks empty"); } if (nextEntry.getName().toUpperCase().endsWith(".IFCXML")) { IfcModelInterface model = null; FakeClosingInputStream fakeClosingInputStream = new FakeClosingInputStream(zipInputStream); model = read(fakeClosingInputStream); if (model.size() == 0) { throw new DeserializeException("Uploaded file does not seem to be a correct IFC file"); } if (zipInputStream.getNextEntry() != null) { zipInputStream.close(); throw new DeserializeException("Zip files may only contain one IFC-file, this zip-file contains more files"); } else { zipInputStream.close(); return model; } } else { throw new DeserializeException("Zip files must contain exactly one IFC-file, this zip-file seems to have one or more non-IFC files"); } } catch (IOException e) { throw new DeserializeException(e); } } else { return read(inputStream); } } private IfcModel read(InputStream inputStream) throws DeserializeException { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); try { XMLStreamReader reader = inputFactory.createXMLStreamReader(inputStream, "UTF-8"); parseDocument(reader); return model; } catch (XMLStreamException e) { new DeserializeException(e); } return null; } private void parseDocument(XMLStreamReader reader) throws DeserializeException { try { while (reader.hasNext()) { reader.next(); if (reader.getEventType() == XMLStreamReader.START_ELEMENT) { if (reader.getLocalName().equalsIgnoreCase("iso_10303_28")) { parseIso_10303_28(reader); } } } } catch (XMLStreamException e) { throw new DeserializeException(e); } } private void parseIso_10303_28(XMLStreamReader reader) throws DeserializeException { try { while (reader.hasNext()) { reader.next(); if (reader.getEventType() == XMLStreamReader.START_ELEMENT) { if (reader.getLocalName().equalsIgnoreCase("uos")) { parseUos(reader); } } else if (reader.getEventType() == XMLStreamReader.END_ELEMENT) { if (reader.getLocalName().equals("iso_10303_28")) { return; } } } } catch (XMLStreamException e) { throw new DeserializeException(e); } } private void parseUos(XMLStreamReader reader) throws DeserializeException { try { while (reader.hasNext()) { reader.next(); if (reader.getEventType() == XMLStreamReader.START_ELEMENT) { parseObject(reader); } else if (reader.getEventType() == XMLStreamReader.END_ELEMENT) { if (reader.getLocalName().equalsIgnoreCase("uos")) { return; } } } } catch (XMLStreamException e) { throw new DeserializeException(e); } } private IdEObject parseObject(XMLStreamReader reader) throws DeserializeException { String className = reader.getLocalName(); EClassifier eClassifier = Ifc2x3tc1Package.eINSTANCE.getEClassifier(className); if (eClassifier == null || !(eClassifier instanceof EClass)) { throw new DeserializeException("No class with name " + className + " was found"); } String id = reader.getAttributeValue("", "id"); if (id == null) { throw new DeserializeException("No id attribute found on " + className); } if (!id.startsWith("i")) { throw new DeserializeException("Id " + id + " is not starting with the letter 'i'"); } EClass eClass = (EClass) eClassifier; long oid = Long.parseLong(id.substring(1)); IdEObject object; if (model.contains(oid)) { object = model.get(oid); } else { object = (IdEObject) Ifc2x3tc1Factory.eINSTANCE.create(eClass); try { model.add(oid, object); } catch (IfcModelInterfaceException e) { throw new DeserializeException(e); } } try { while (reader.hasNext()) { reader.next(); if (reader.getEventType() == XMLStreamReader.START_ELEMENT) { parseField(object, reader); } else if (reader.getEventType() == XMLStreamReader.END_ELEMENT) { if (reader.getLocalName().equalsIgnoreCase(className)) { return object; } } } } catch (XMLStreamException e) { throw new DeserializeException(e); } return object; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void parseField(IdEObject object, XMLStreamReader reader) throws DeserializeException { String fieldName = reader.getLocalName(); EStructuralFeature eStructuralFeature = object.eClass().getEStructuralFeature(fieldName); if (eStructuralFeature == null) { throw new DeserializeException("Field " + fieldName + " not found on class " + object.eClass().getName()); } EClassifier realType = null; try { while (reader.hasNext()) { reader.next(); if (reader.getEventType() == XMLStreamReader.START_ELEMENT) { if (reader.getAttributeValue("", "id") != null) { IdEObject reference = parseObject(reader); if (eStructuralFeature.isMany()) { ((List) object.eGet(eStructuralFeature)).add(reference); } else { object.eSet(eStructuralFeature, reference); } } else if (reader.getAttributeValue("", "ref") != null) { String ref = reader.getAttributeValue("", "ref"); if (!ref.startsWith("i")) { throw new DeserializeException("Reference id " + ref + " should start with an 'i'"); } Long refId = Long.parseLong(ref.substring(1)); IdEObject reference = null; if (!model.contains(refId)) { String referenceType = reader.getLocalName(); reference = (IdEObject) Ifc2x3tc1Factory.eINSTANCE.create((EClass) Ifc2x3tc1Package.eINSTANCE.getEClassifier(referenceType)); try { model.add(refId, reference); } catch (IfcModelInterfaceException e) { throw new DeserializeException(e); } } else { reference = model.get(refId); } if (eStructuralFeature.isMany()) { List list = (List) object.eGet(eStructuralFeature); String posString = reader.getAttributeValue("urn:iso.org:standard:10303:part(28):version(2):xmlschema:common", "pos"); if (posString == null) { list.add(reference); } else { int pos = Integer.parseInt(posString); if (list.size() > pos) { list.set(pos, reference); } else { for (int i = list.size() - 1; i < pos - 1; i++) { list.add(reference.eClass().getEPackage().getEFactoryInstance().create(reference.eClass())); } list.add(reference); } } } else { object.eSet(eStructuralFeature, reference); } } else { String realTypeString = reader.getLocalName(); realType = Ifc2x3tc1Package.eINSTANCE.getEClassifier(realTypeString); } } else if (reader.getEventType() == XMLStreamReader.END_ELEMENT) { if (reader.getLocalName().equalsIgnoreCase(fieldName)) { return; } if (realType != null && reader.getLocalName().equalsIgnoreCase(realType.getName())) { realType = null; } } else if (reader.getEventType() == XMLStreamReader.CHARACTERS) { if (!reader.isWhiteSpace()) { String text = reader.getText(); if (eStructuralFeature.getEType() instanceof EDataType) { if (eStructuralFeature.isMany()) { String[] split = text.split(" "); List list = (List) object.eGet(eStructuralFeature); for (String s : split) { list.add(parsePrimitive(eStructuralFeature.getEType(), s)); } } else { object.eSet(eStructuralFeature, parsePrimitive(eStructuralFeature.getEType(), text)); } } else { if (realType == null) { realType = eStructuralFeature.getEType(); } if (realType instanceof EClass) { EClass eClass = (EClass) realType; if (eClass.getEAnnotation("wrapped") != null) { IdEObject wrappedObject = (IdEObject) Ifc2x3tc1Factory.eINSTANCE.create(eClass); // model.add(wrappedObject); EStructuralFeature wrappedValueFeature = eClass.getEStructuralFeature("wrappedValue"); wrappedObject.eSet(wrappedValueFeature, parsePrimitive(wrappedValueFeature.getEType(), text)); if (wrappedValueFeature.getEType() == EcorePackage.eINSTANCE.getEDouble()) { EStructuralFeature doubleStringFeature = eClass.getEStructuralFeature("wrappedValueAsString"); wrappedObject.eSet(doubleStringFeature, text); } List list = (List) object.eGet(eStructuralFeature); if (eStructuralFeature.isMany()) { list.add(wrappedObject); } else { object.eSet(eStructuralFeature, wrappedObject); } } } else { if (eStructuralFeature.isMany()) { String[] split = text.split(" "); List list = (List) object.eGet(eStructuralFeature); for (String s : split) { list.add(parsePrimitive(realType, s)); } } else { object.eSet(eStructuralFeature, parsePrimitive(realType, text)); } } } } } } } catch (NumberFormatException e) { throw new DeserializeException(e); } catch (XMLStreamException e) { throw new DeserializeException(e); } } private Object parsePrimitive(EClassifier eType, String text) throws DeserializeException { if (eType == EcorePackage.eINSTANCE.getEString()) { return text; } else if (eType == EcorePackage.eINSTANCE.getEInt()) { return Integer.parseInt(text); } else if (eType == EcorePackage.eINSTANCE.getEDouble()) { return Double.parseDouble(text); } else if (eType == EcorePackage.eINSTANCE.getEBoolean()) { return Boolean.parseBoolean(text); } else if (eType instanceof EEnum) { EEnumLiteral eEnumLiteral = ((EEnum) eType).getEEnumLiteral(text.toUpperCase()); if (eEnumLiteral == null) { if (text.equals("unknown")) { return null; } else { throw new DeserializeException("Unknown enum literal " + text + " in enum " + ((EEnum) eType).getName()); } } return eEnumLiteral.getInstance(); } else { throw new DeserializeException("Unimplemented primitive type: " + eType.getName()); } } @Override public void init(SchemaDefinition schema) { } }