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