Index: applications/editors/josm/plugins/poly/src/poly/PolyExporter.java
===================================================================
--- applications/editors/josm/plugins/poly/src/poly/PolyExporter.java	(revision 34546)
+++ applications/editors/josm/plugins/poly/src/poly/PolyExporter.java	(revision 34860)
@@ -2,19 +2,17 @@
 package poly;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.HashSet;
 import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
+import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
@@ -28,7 +26,11 @@
  *
  * @author zverik
+ * @author Gerd Petermann
  */
 public class PolyExporter extends OsmExporter {
 
+    /**
+     * Create exporter.
+     */
     public PolyExporter() {
         super(PolyType.FILE_FILTER);
@@ -38,34 +40,35 @@
     public void exportData(File file, Layer layer) throws IOException {
         if (layer instanceof OsmDataLayer) {
-            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"))) {
+            if (((OsmDataLayer) layer).getDataSet().getWays().stream().anyMatch(w -> !w.isClosed())) {
+                throw new IOException(tr("Data contains unclosed ways."));
+            }
+            try (BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
                 DataSet ds = ((OsmDataLayer) layer).getDataSet();
-                Map<Way, Boolean> ways = new TreeMap<>();
-                String polygonName = file.getName();
-                if (polygonName.indexOf('.') > 0)
-                    polygonName = polygonName.substring(0, polygonName.indexOf('.'));
-                for (Way w : ds.getWays()) {
-                    if (w.isClosed()) {
-                        boolean outer = isOuter(w);
-                        ways.put(w, outer);
-                        if (w.hasKey("name"))
-                            polygonName = w.get("name").replace("\n", " ");
+                HashSet<Way> written = new HashSet<>();
+                boolean firstFile = true;
+                String fileName = file.getName();
+                if (fileName.lastIndexOf('.') > 0) {
+                    // remove extension
+                    fileName = fileName.substring(0, fileName.lastIndexOf('.'));
+                }
+
+                for (Relation rel : ds.getRelations()) {
+                    if (rel.isMultipolygon()) {
+                        if (!firstFile) {
+                            writer.newLine();
+                        }
+                        writeRelation(writer, fileName, rel, written);
+                        firstFile = false;
                     }
                 }
-                ways = sortOuterInner(ways);
-
+                
+                if (firstFile) {
+                    writer.write(fileName);
+                }
                 int counter = 1;
-                writer.write(polygonName);
-                writer.newLine();
-                for (Way w : ways.keySet()) {
-                    if (!ways.get(w))
-                        writer.write('!');
-                    writer.write(String.valueOf(counter++));
-                    writer.newLine();
-                    for (Node n : w.getNodes()) {
-                        writer.write(String.format(Locale.ENGLISH, "   %f   %f", n.getCoor().lon(), n.getCoor().lat()));
-                        writer.newLine();
+                for (Way w : ds.getWays()) {
+                    if (!written.contains(w)) {
+                        writeWay(writer, w, counter);
                     }
-                    writer.write("END");
-                    writer.newLine();
                 }
                 writer.write("END");
@@ -75,31 +78,37 @@
     }
 
-    private boolean isOuter(Way w) {
-        for (OsmPrimitive p : w.getReferrers()) {
-            if (p instanceof Relation && ((Relation) p).isMultipolygon()) {
-                for (RelationMember m : ((Relation) p).getMembers()) {
-                    if (m.refersTo(w) && "inner".equals(m.getRole())) {
-                        return false;
-                    }
-                }
+    private static void writeRelation(BufferedWriter writer, String fileName, Relation rel, Set<Way> written) throws IOException {
+        String polygonName = fileName;
+        if (rel.getName() != null)
+            polygonName = rel.getName();
+        writer.write(polygonName);
+        writer.newLine();
+        int counter = 1;
+        for (RelationMember rm: rel.getMembers()) {
+            if (rm.isWay()) {
+                if ("inner".equals(rm.getRole()))
+                    writer.write('!');
+                Way w = rm.getWay();
+                counter = writeWay(writer, w, counter);
+                written.add(w);
             }
         }
-        return true;
     }
 
-    private Map<Way, Boolean> sortOuterInner(Map<Way, Boolean> ways) {
-        LinkedHashMap<Way, Boolean> result = new LinkedHashMap<>(ways.size());
-        List<Way> inner = new ArrayList<>();
-        for (Way w : ways.keySet()) {
-            Boolean outer = ways.get(w);
-            if (outer)
-                result.put(w, outer);
-            else
-                inner.add(w);
+    private static int writeWay(BufferedWriter writer, Way w, int counter) throws IOException {
+        String name = w.getName();
+        if (name == null) {
+            name = String.valueOf(counter++);
+        } 
+        writer.write(name);
+        writer.newLine();
+
+        for (Node n : w.getNodes()) {
+            writer.write(String.format(Locale.ENGLISH, "   %f   %f", n.getCoor().lon(), n.getCoor().lat()));
+            writer.newLine();
         }
-        for (Way w : inner) {
-            result.put(w, Boolean.FALSE);
-        }
-        return result;
+        writer.write("END");
+        writer.newLine();
+        return counter;
     }
 }
Index: applications/editors/josm/plugins/poly/src/poly/PolyImporter.java
===================================================================
--- applications/editors/josm/plugins/poly/src/poly/PolyImporter.java	(revision 34546)
+++ applications/editors/josm/plugins/poly/src/poly/PolyImporter.java	(revision 34860)
@@ -8,4 +8,5 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -27,5 +28,4 @@
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.xml.sax.SAXException;
 
 /**
@@ -33,12 +33,18 @@
  *
  * @author zverik
+ * @author Gerd Petermann
  */
 public class PolyImporter extends OsmImporter {
+    /**
+     * Create importer.
+     */
     public PolyImporter() {
         super(PolyType.FILE_FILTER);
     }
 
-    protected DataSet parseDataSet(final String source) throws IOException, SAXException, IllegalDataException {
-        return parseDataSet(new CachedFile(source).getInputStream(), NullProgressMonitor.INSTANCE);
+    protected DataSet parseDataSet(final String source) throws IOException, IllegalDataException {
+        try (CachedFile cf = new CachedFile(source)) {
+            return parseDataSet(cf.getInputStream(), NullProgressMonitor.INSTANCE);
+        }
     }
 
@@ -49,6 +55,6 @@
         CheckParameterUtil.ensureParameterNotNull(in, "in");
 
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF8"))) {
-            progressMonitor.beginTask(tr("Reading polygon filter file..."), 2);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
+            progressMonitor.beginTask(tr("Reading polygon filter file..."), 3); 
             progressMonitor.indeterminateSubTask(tr("Reading polygon filter file..."));
             List<Area> areas = loadPolygon(reader);
@@ -66,5 +72,5 @@
     }
 
-    private List<Area> loadPolygon(BufferedReader reader) throws IllegalDataException, IOException {
+    private static List<Area> loadPolygon(BufferedReader reader) throws IllegalDataException, IOException {
         String name = reader.readLine();
         if (name == null || name.trim().length() == 0)
@@ -76,46 +82,48 @@
         while (true) {
             String line;
-            do {
-                line = reader.readLine();
-                if (line == null)
-                    throw new IllegalDataException("File ended prematurely without END record");
-                line = line.trim();
-            } while (line.length() == 0);
-
-            if (line.equals("END")) {
-                if (!parsingSection)
-                    break;
-                else {
+            line = reader.readLine();
+            if (line == null) 
+                break;
+            line = line.trim();
+            if (line.isEmpty()) {
+                // empty line is only allowed after a complete file (one or more rings belonging to one polygon)
+                if (parsingSection)
+                    throw new IllegalDataException(tr("Empty line in coordinate section"));
+                area = null;
+                parsingSection = false;
+                name = null;
+            } else if (line.equals("END")) {
+                if (!parsingSection) {
+                    area = null;
+                } else {
                     // an area has been read
                     if (area.getNodeCount() < 2)
                         throw new IllegalDataException(tr("There are less than 2 points in an area"));
                     areas.add(area);
-                    if (areas.size() == 1)
-                        areas.get(0).setPolygonName(name);
+                    area.setPolygonName(name);
                     parsingSection = false;
                 }
-            } else {
+            } else if (name == null) {
+                name = line;
+            } else if (line.length() > 0) {
                 if (!parsingSection) {
+                    if (line.indexOf(' ') >= 0) {
+                        boolean coordInsteadOfName = false;
+                        try {
+                            LatLon ll = parseCoordinate(line); 
+                            if (ll.isValid()) {
+                                coordInsteadOfName = true;
+                            }
+                        } catch (IllegalDataException e) {
+                            coordInsteadOfName = false;
+                        }
+                        if (coordInsteadOfName) {
+                            throw new IllegalDataException(tr("Found coordinates ''{0}'' instead of name", line));
+                        }
+                    }
                     area = new Area(line);
                     parsingSection = true;
                 } else {
-                    // reading area, parse coordinates
-                    String[] tokens = line.split("\\s+");
-                    double[] coords = new double[2];
-                    int tokenCount = 0;
-                    for (String token : tokens) {
-                        if (token.length() > 0) {
-                            if (tokenCount > 2)
-                                throw new IllegalDataException(tr("A polygon coordinate line must contain exactly 2 numbers"));
-                            try {
-                                coords[tokenCount++] = Double.parseDouble(token);
-                            } catch (NumberFormatException e) {
-                                throw new IllegalDataException(tr("Unable to parse {0} as a number", token));
-                            }
-                        }
-                    }
-                    if (tokenCount < 2)
-                        throw new IllegalDataException(tr("A polygon coordinate line must contain exactly 2 numbers"));
-                    LatLon coord = new LatLon(coords[1], coords[0]);
+                    LatLon coord = parseCoordinate(line);
                     if (!coord.isValid()) {
                         // fix small deviations
@@ -126,10 +134,10 @@
                         if (lat < -90.0 && lat > -95.0) lat = -90.0;
                         if (lat > 90.0 && lat < 95.0) lat = 90.0;
+                        coord = new LatLon(lat, lon);
                         fixedCoords++;
-                        coord = new LatLon(lat, lon);
                         if (!coord.isValid())
                             throw new IllegalDataException(tr("Invalid coordinates were found: {0}, {1}", coord.lat(), coord.lon()));
                     }
-                    area.addNode(coord);
+                    area.addNode(parseCoordinate(line));
                 }
             }
@@ -141,18 +149,63 @@
     }
 
-    private DataSet constructDataSet(List<Area> areas) {
+    /**
+     * Parse a line that should contain two double values which describe a latitude/longitude pair. 
+     * @param line the line to parse
+     * @return a new LatLon 
+     * @throws IllegalDataException in case of error
+     */
+    private static LatLon parseCoordinate(String line) throws IllegalDataException {
+        String[] tokens = line.split("\\s+");
+        double[] coords = new double[2];
+        int tokenCount = 0;
+        for (String token : tokens) {
+            if (token.length() > 0) {
+                if (tokenCount > 2)
+                    break;
+                try {
+                    coords[tokenCount++] = Double.parseDouble(token);
+                } catch (NumberFormatException e) {
+                    throw new IllegalDataException(tr("Unable to parse {0} as a number", token));
+                }
+            }
+        }
+        if (tokenCount != 2)
+            throw new IllegalDataException(tr("A polygon coordinate line must contain exactly 2 numbers"));
+        return new LatLon(coords[1], coords[0]);
+    }
+
+    private static DataSet constructDataSet(List<Area> areas) {
         DataSet ds = new DataSet();
         ds.setUploadPolicy(UploadPolicy.DISCOURAGED);
 
-        boolean foundInner = false;
+        
+        List<Area> curretSet = new ArrayList<>();
+        
         for (Area area : areas) {
-            if (!area.isOuter())
-                foundInner = true;
-            area.constructWay(ds);
-        }
-
-        if (foundInner) {
+            if (!curretSet.isEmpty() && !area.polygonName.equals(curretSet.get(0).polygonName)) {
+                constructPrimitive(ds, curretSet);
+                curretSet.clear();
+            }
+            curretSet.add(area);
+        }
+        if (!curretSet.isEmpty())
+            constructPrimitive(ds, curretSet);
+
+        return ds;
+    }
+
+    private static void constructPrimitive(DataSet ds, List<Area> areas) {
+        boolean isMultipolygon = areas.size() > 1;
+        for (Area area : areas) {
+            area.constructWay(ds, isMultipolygon);
+        }
+
+        if (isMultipolygon) {
             Relation mp = new Relation();
             mp.put("type", "multipolygon");
+            Area outer = areas.get(0);
+            if (outer.polygonName != null) {
+                mp.put("name", outer.polygonName);
+            }
             for (Area area : areas) {
                 mp.addMember(new RelationMember(area.isOuter() ? "outer" : "inner", area.getWay()));
@@ -160,6 +213,4 @@
             ds.addPrimitive(mp);
         }
-
-        return ds;
     }
 
@@ -178,9 +229,11 @@
             nodes = new ArrayList<>();
             way = null;
-            polygonName = null;
+            polygonName = "";
         }
 
         public void setPolygonName(String polygonName) {
-            this.polygonName = polygonName;
+            if (polygonName != null) {
+                this.polygonName = polygonName;
+            }
         }
 
@@ -202,5 +255,5 @@
         }
 
-        public void constructWay(DataSet ds) {
+        public void constructWay(DataSet ds, boolean isMultipolygon) {
             way = new Way();
             for (LatLon coord : nodes) {
@@ -210,6 +263,10 @@
             }
             way.addNode(way.getNode(0));
-            if (polygonName != null)
-                way.put("name", polygonName);
+            if (isMultipolygon && name != null)
+                way.put("name", name);
+            else {
+                if (polygonName != null)
+                    way.put("name", polygonName);
+            }
             ds.addPrimitive(way);
         }
Index: applications/editors/josm/plugins/poly/src/poly/PolyType.java
===================================================================
--- applications/editors/josm/plugins/poly/src/poly/PolyType.java	(revision 34546)
+++ applications/editors/josm/plugins/poly/src/poly/PolyType.java	(revision 34860)
@@ -10,8 +10,13 @@
  *
  * @author zverik
+ * @author Gerd Petermann
  */
-public interface PolyType {
-    String EXTENSION = "poly";
-    ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+
+final class PolyType {
+    private static final String EXTENSION = "poly";
+    
+    /** filter for osmosis poly files */
+    static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
             EXTENSION, EXTENSION, tr("Osmosis polygon filter files") + " (*." + EXTENSION + ")");
+    private PolyType() {}
 }
