diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index e5a1b3b3cafbb84016d1d62d87d963d10e4aec10..bdc2454adaa7ee4b11db76ed041f7ad86dd9bd1e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -204,26 +204,46 @@ public class CityGmlParser { CityGMLContext context = getContext(); if (config.getValidate()) { - //TODO: Think of something to validate Inputstream + //TODO: Think of something to XML-validate Inputstream + + //XML validation requires looking at nesting of tags + //not a problem for small files, but big files will run into memory limit and crash + // Maybe do it dirty by writing stream to temp file and passing it to parseCityGmlFile()? throw new InvalidGmlFileException("Invalid GML File."); } try { - parseEpsgCodeFromStream(is, config); + BufferedInputStream bis = new BufferedInputStream(is); + readEpsgCodeFromInputStream(bis,config); CityGMLInputFactory in = context.createCityGMLInputFactory() .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - try(ObservedInputStream ois = new ObservedInputStream(is, is.available())){ + try(ObservedInputStream ois = new ObservedInputStream(bis, bis.available())){ if (l != null){ ois.addListener(l::updateProgress); } return readAndKeepFeatures(config, Path.of(""), in, ois); } - } catch (CityGMLReadException | IOException | ParserConfigurationException | SAXException e) { - logger.error(e); + } catch (CityGMLReadException | IOException e) { throw new CityGmlParseException("Failed to read CityGML file", e); } } + private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException { + try{ + // Mark start position of GML-"file" + bis.mark(Integer.MAX_VALUE); + // Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range + int peekingWidth = Math.min(10000, bis.available()); + byte[] buf = new byte[peekingWidth]; + bis.read(buf, 0, peekingWidth); + bis.reset(); + parseEpsgCodeFromBuffer(buf, config); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML file", e); + } + + } + public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { Path f = Paths.get(file); @@ -438,6 +458,31 @@ public class CityGmlParser { logEpsgParseError(e); } } + + private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config) + throws ParserConfigurationException, SAXException { + InputStream is = new ByteArrayInputStream(buffer); + SAXParser parser = FACTORY.newSAXParser(); + CityGmlHandler handler = new CityGmlHandler(); + try { + parser.parse(new InputSource(is), handler); + } catch (EnvelopeFoundException e) { + try { + parseCoordinateSystem(config, handler); + + } catch (Exception e2) { + logEpsgParseError(e2); + } + }catch (SAXParseException spe){ + // suppress XML document structure warning + if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")){ + logEpsgParseError(spe); + } + }catch (Exception e) { + logEpsgParseError(e); + } + } + private static void logEpsgParseError(Exception e){ logger.debug("Exception while parsing for EPSG code", e); if (logger.isWarnEnabled()) {