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