001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.io.InputStream; 008import java.io.InputStreamReader; 009 010import javax.xml.parsers.ParserConfigurationException; 011import javax.xml.parsers.SAXParserFactory; 012 013import org.openstreetmap.josm.data.osm.history.HistoryDataSet; 014import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive; 015import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 016import org.openstreetmap.josm.gui.progress.ProgressMonitor; 017import org.openstreetmap.josm.tools.CheckParameterUtil; 018import org.xml.sax.Attributes; 019import org.xml.sax.InputSource; 020import org.xml.sax.SAXException; 021 022/** 023 * Parser for OSM history data. 024 * 025 * It is slightly different from {@link OsmReader} because we don't build an internal graph of 026 * {@link org.openstreetmap.josm.data.osm.OsmPrimitive}s. We use objects derived from 027 * {@link HistoryOsmPrimitive} instead and we keep the data in a dedicated {@link HistoryDataSet}. 028 * @since 1670 029 */ 030public class OsmHistoryReader { 031 032 private final InputStream in; 033 private final HistoryDataSet data; 034 035 private class Parser extends AbstractParser { 036 037 protected String getCurrentPosition() { 038 if (locator == null) 039 return ""; 040 return "(" + locator.getLineNumber() + "," + locator.getColumnNumber() + ")"; 041 } 042 043 protected void throwException(String message) throws SAXException { 044 throw new SAXException(getCurrentPosition() + message); 045 } 046 047 @Override 048 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 049 doStartElement(qName, atts); 050 } 051 052 @Override 053 public void endElement(String uri, String localName, String qName) throws SAXException { 054 if (qName.equals("node") 055 || qName.equals("way") 056 || qName.equals("relation")) { 057 data.put(currentPrimitive); 058 } 059 } 060 } 061 062 /** 063 * Constructs a new {@code OsmHistoryReader}. 064 * 065 * @param source the input stream with the history content as XML document. Must not be null. 066 * @throws IllegalArgumentException if source is {@code null}. 067 */ 068 public OsmHistoryReader(InputStream source) { 069 CheckParameterUtil.ensureParameterNotNull(source, "source"); 070 this.in = source; 071 this.data = new HistoryDataSet(); 072 } 073 074 /** 075 * Parses the content. 076 * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null 077 * @return the parsed data 078 * @throws SAXException If any SAX errors occur during processing. 079 * @throws IOException If any IO errors occur. 080 */ 081 public HistoryDataSet parse(ProgressMonitor progressMonitor) throws SAXException, IOException { 082 InputSource inputSource = new InputSource(new InputStreamReader(in, "UTF-8")); 083 progressMonitor.beginTask(tr("Parsing OSM history data ...")); 084 try { 085 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser()); 086 } catch (ParserConfigurationException e) { 087 e.printStackTrace(); // broken SAXException chaining 088 throw new SAXException(e); 089 } finally { 090 progressMonitor.finishTask(); 091 } 092 return data; 093 } 094}