package maps.gml; import java.io.File; import java.io.Reader; import java.io.FileReader; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.dom4j.io.OutputFormat; import maps.MapFormat; import maps.MapException; import maps.Map; import rescuecore2.log.Logger; /** Abstract base class for map formats that use GML. */ public abstract class GMLMapFormat implements MapFormat { @Override public GMLMap read(File file) throws MapException { FileReader r; try { r = new FileReader(file); } catch (FileNotFoundException e) { throw new MapException(e); } try { return read(r); } catch (DocumentException e) { throw new MapException(e); } finally { try { r.close(); } catch (IOException e) { Logger.warn("IOException while closing file reader", e); } } } @Override public void write(Map map, File file) throws MapException { if (map == null) { throw new IllegalArgumentException("Map must not be null"); } if (file == null) { throw new IllegalArgumentException("File must not be null"); } if (!(map instanceof GMLMap)) { throw new IllegalArgumentException("Map is not a GMLMap: " + map.getClass().getName()); } Document doc = write((GMLMap)map); try { if (!file.exists()) { File parent = file.getParentFile(); if (!parent.exists()) { if (!file.getParentFile().mkdirs()) { throw new MapException("Couldn't create file " + file.getPath()); } } if (!file.createNewFile()) { throw new MapException("Couldn't create file " + file.getPath()); } } XMLWriter writer = new XMLWriter(new FileOutputStream(file), OutputFormat.createPrettyPrint()); Element root = doc.getRootElement(); for (java.util.Map.Entry<String, String> next : getNamespaces().entrySet()) { root.addNamespace(next.getKey(), next.getValue()); } writer.write(doc); writer.flush(); writer.close(); } catch (IOException e) { throw new MapException(e); } } @Override public boolean canRead(File file) throws MapException { if (file.isDirectory() || !file.exists()) { return false; } if (!file.getName().endsWith(".gml")) { return false; } // Check that the XML dialect is correct by looking at the root element. FileReader r; try { r = new FileReader(file); } catch (FileNotFoundException e) { throw new MapException(e); } try { XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(r); while (reader.hasNext()) { if (reader.next() == XMLStreamConstants.START_ELEMENT) { return isCorrectRootElement(reader.getNamespaceURI(), reader.getLocalName()); } } } catch (XMLStreamException e) { Logger.debug("Exception while reading XML stream", e); return false; } finally { try { r.close(); } catch (IOException e) { Logger.warn("IOException while closing file reader", e); } } return false; } /** Read a GMLMap from a Reader. @param reader The Reader to read. @return A new GMLMap. @throws DocumentException If there is a problem parsing the XML. @throws MapException If there is a problem reading the map. */ public GMLMap read(Reader reader) throws DocumentException, MapException { Logger.debug("Parsing GML"); SAXReader saxReader = new SAXReader(); Document doc = saxReader.read(reader); Logger.debug("Building map"); return read(doc); } /** Find out if the root element is correct for this format type. @param uri The URI of the root element. @param localName The local name of the root element. @return True if the uri and localName are correct for this format's root element, false otherwise. */ protected abstract boolean isCorrectRootElement(String uri, String localName); /** Read a Document and return a GMLMap. @param doc The document to read. @return A new GMLMap. @throws MapException If there is a problem reading the map. */ protected abstract GMLMap read(Document doc) throws MapException; /** Turn a GMLMap into an xml document. @param map The map to write. @return A new document. */ protected abstract Document write(GMLMap map); /** Get the uris and preferred prefixes for all namespaces this format cares about. @return A map from prefix to uri for all relevant namespaces. */ protected abstract java.util.Map<String, String> getNamespaces(); }