001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.io.Serializable; 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.Map; 008 009import org.openstreetmap.josm.gui.DefaultNameFormatter; 010 011/** 012 * Comparator, comparing pritimives by:<ul> 013 * <li>type and ids in "quick" mode</li> 014 * <li>type and objects display names instead</li> 015 * </ul> 016 * @since 4113 017 */ 018public class OsmPrimitiveComparator implements Comparator<OsmPrimitive>, Serializable { 019 020 private static final long serialVersionUID = 1L; 021 022 private final Map<OsmPrimitive, String> cache = new HashMap<>(); 023 private final boolean relationsFirst; 024 private final boolean quick; 025 026 /** 027 * Constructs a new {@code OsmPrimitiveComparator}. 028 */ 029 public OsmPrimitiveComparator() { 030 this(false, false); 031 } 032 033 /** 034 * Constructs a new {@code OsmPrimitiveComparator}. 035 * @param quick if {@code true}, sorts by type and ids (fast), otherwise sort by type and display names (slower) 036 * @param relationsFirst if {@code true}, always list relations first 037 */ 038 public OsmPrimitiveComparator(boolean quick, boolean relationsFirst) { 039 this.quick = quick; 040 this.relationsFirst = relationsFirst; 041 } 042 043 private String cachedName(OsmPrimitive p) { 044 String name = cache.get(p); 045 if (name == null) { 046 name = p.getDisplayName(DefaultNameFormatter.getInstance()); 047 cache.put(p, name); 048 } 049 return name; 050 } 051 052 private int compareName(OsmPrimitive a, OsmPrimitive b) { 053 String an = cachedName(a); 054 String bn = cachedName(b); 055 // make sure display names starting with digits are the end of the list 056 if (Character.isDigit(an.charAt(0)) && Character.isDigit(bn.charAt(0))) 057 return an.compareTo(bn); 058 else if (Character.isDigit(an.charAt(0)) && !Character.isDigit(bn.charAt(0))) 059 return 1; 060 else if (!Character.isDigit(an.charAt(0)) && Character.isDigit(bn.charAt(0))) 061 return -1; 062 return an.compareTo(bn); 063 } 064 065 private static int compareId(OsmPrimitive a, OsmPrimitive b) { 066 long idA = a.getUniqueId(); 067 long idB = b.getUniqueId(); 068 if (idA < idB) return -1; 069 if (idA > idB) return 1; 070 return 0; 071 } 072 073 private int compareType(OsmPrimitive a, OsmPrimitive b) { 074 if (relationsFirst) { 075 // show relations before ways, then nodes 076 if (a.getType().equals(OsmPrimitiveType.RELATION)) return -1; 077 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1; 078 // a is a way 079 if (b.getType().equals(OsmPrimitiveType.RELATION)) return 1; 080 // b is a node 081 } else { 082 // show ways before relations, then nodes 083 if (a.getType().equals(OsmPrimitiveType.WAY)) return -1; 084 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1; 085 // a is a relation 086 if (b.getType().equals(OsmPrimitiveType.WAY)) return 1; 087 // b is a node 088 } 089 return -1; 090 } 091 092 @Override 093 public int compare(OsmPrimitive a, OsmPrimitive b) { 094 if (a.getType().equals(b.getType())) 095 return quick ? compareId(a, b) : compareName(a, b); 096 return compareType(a, b); 097 } 098}