source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java@ 10611

Last change on this file since 10611 was 10611, checked in by Don-vip, 8 years ago

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

  • Property svn:eol-style set to native
File size: 8.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.sort;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.LinkedHashMap;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.Map;
12import java.util.Map.Entry;
13
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.RelationMember;
17import org.openstreetmap.josm.gui.DefaultNameFormatter;
18import org.openstreetmap.josm.tools.AlphanumComparator;
19import org.openstreetmap.josm.tools.Utils;
20
21public class RelationSorter {
22
23 private interface AdditionalSorter {
24 boolean acceptsMember(RelationMember m);
25
26 List<RelationMember> sortMembers(List<RelationMember> list);
27 }
28
29 private static final Collection<AdditionalSorter> additionalSorters = new ArrayList<>();
30 static {
31 // first adequate sorter is used, so order matters
32 additionalSorters.add(new AssociatedStreetRoleStreetSorter());
33 additionalSorters.add(new AssociatedStreetRoleAddressHouseSorter());
34 additionalSorters.add(new PublicTransportRoleStopPlatformSorter());
35 }
36
37 /**
38 * Class that sorts the {@code street} members of
39 * {@code type=associatedStreet} and {@code type=street} relations.
40 */
41 private static class AssociatedStreetRoleStreetSorter implements AdditionalSorter {
42
43 @Override
44 public boolean acceptsMember(RelationMember m) {
45 return "street".equals(m.getRole());
46 }
47
48 @Override
49 public List<RelationMember> sortMembers(List<RelationMember> list) {
50 return sortMembersByConnectivity(list);
51 }
52 }
53
54 /**
55 * Class that sorts the {@code address} and {@code house} members of
56 * {@code type=associatedStreet} and {@code type=street} relations.
57 */
58 private static class AssociatedStreetRoleAddressHouseSorter implements AdditionalSorter {
59
60 @Override
61 public boolean acceptsMember(RelationMember m) {
62 return "address".equals(m.getRole()) || "house".equals(m.getRole());
63 }
64
65 @Override
66 public List<RelationMember> sortMembers(List<RelationMember> list) {
67 Collections.sort(list, (a, b) -> {
68 final int houseNumber = AlphanumComparator.getInstance().compare(
69 a.getMember().get("addr:housenumber"),
70 b.getMember().get("addr:housenumber"));
71 if (houseNumber != 0) {
72 return houseNumber;
73 }
74 final String aDisplayName = a.getMember().getDisplayName(DefaultNameFormatter.getInstance());
75 final String bDisplayName = b.getMember().getDisplayName(DefaultNameFormatter.getInstance());
76 return AlphanumComparator.getInstance().compare(aDisplayName, bDisplayName);
77 });
78 return list;
79 }
80 }
81
82 /**
83 * Class that sorts the {@code platform} and {@code stop} members of
84 * {@code type=public_transport} relations.
85 */
86 private static class PublicTransportRoleStopPlatformSorter implements AdditionalSorter {
87
88 @Override
89 public boolean acceptsMember(RelationMember m) {
90 return m.getRole() != null && (m.getRole().startsWith("platform") || m.getRole().startsWith("stop"));
91 }
92
93 private static String getStopName(OsmPrimitive p) {
94 for (Relation ref : Utils.filteredCollection(p.getReferrers(), Relation.class)) {
95 if (ref.hasTag("type", "public_transport") && ref.hasTag("public_transport", "stop_area") && ref.getName() != null) {
96 return ref.getName();
97 }
98 }
99 return p.getName();
100 }
101
102 @Override
103 public List<RelationMember> sortMembers(List<RelationMember> list) {
104 final Map<String, RelationMember> platformByName = new HashMap<>();
105 for (RelationMember i : list) {
106 if (i.getRole().startsWith("platform")) {
107 final RelationMember old = platformByName.put(getStopName(i.getMember()), i);
108 if (old != null) {
109 // Platform with same name present. Stop to avoid damaging complicated relations.
110 // This case can happily be handled differently.
111 return list;
112 }
113 }
114 }
115 final List<RelationMember> sorted = new ArrayList<>(list.size());
116 for (RelationMember i : list) {
117 if (i.getRole().startsWith("stop")) {
118 sorted.add(i);
119 final RelationMember platform = platformByName.remove(getStopName(i.getMember()));
120 if (platform != null) {
121 sorted.add(platform);
122 }
123 }
124 }
125 sorted.addAll(platformByName.values());
126 return sorted;
127 }
128 }
129
130 /**
131 * Sort a collection of relation members by the way they are linked.
132 *
133 * @param relationMembers collection of relation members
134 * @return sorted collection of relation members
135 */
136 public List<RelationMember> sortMembers(List<RelationMember> relationMembers) {
137 List<RelationMember> newMembers = new ArrayList<>();
138
139 // Sort members with custom mechanisms (relation-dependent)
140 List<RelationMember> defaultMembers = new ArrayList<>(relationMembers.size());
141 // Maps sorter to assigned members for sorting. Use LinkedHashMap to retain order.
142 Map<AdditionalSorter, List<RelationMember>> customMap = new LinkedHashMap<>();
143
144 // Dispatch members to the first adequate sorter
145 for (RelationMember m : relationMembers) {
146 boolean wasAdded = false;
147 for (AdditionalSorter sorter : additionalSorters) {
148 if (sorter.acceptsMember(m)) {
149 List<RelationMember> list;
150 list = customMap.get(sorter);
151 if (list == null) {
152 list = new LinkedList<>();
153 customMap.put(sorter, list);
154 }
155 list.add(m);
156 wasAdded = true;
157 break;
158 }
159 }
160 if (!wasAdded) {
161 defaultMembers.add(m);
162 }
163 }
164
165 // Sort members and add them to result
166 for (Entry<AdditionalSorter, List<RelationMember>> entry : customMap.entrySet()) {
167 newMembers.addAll(entry.getKey().sortMembers(entry.getValue()));
168 }
169 newMembers.addAll(sortMembersByConnectivity(defaultMembers));
170 return newMembers;
171 }
172
173 public static List<RelationMember> sortMembersByConnectivity(List<RelationMember> defaultMembers) {
174
175 List<RelationMember> newMembers = new ArrayList<>();
176
177 RelationNodeMap map = new RelationNodeMap(defaultMembers);
178 // List of groups of linked members
179 //
180 List<LinkedList<Integer>> allGroups = new ArrayList<>();
181
182 // current group of members that are linked among each other
183 // Two successive members are always linked i.e. have a common node.
184 //
185 LinkedList<Integer> group;
186
187 Integer first;
188 while ((first = map.pop()) != null) {
189 group = new LinkedList<>();
190 group.add(first);
191
192 allGroups.add(group);
193
194 Integer next = first;
195 while ((next = map.popAdjacent(next)) != null) {
196 group.addLast(next);
197 }
198
199 // The first element need not be in front of the list.
200 // So the search goes in both directions
201 //
202 next = first;
203 while ((next = map.popAdjacent(next)) != null) {
204 group.addFirst(next);
205 }
206 }
207
208 for (List<Integer> tmpGroup : allGroups) {
209 for (Integer p : tmpGroup) {
210 newMembers.add(defaultMembers.get(p));
211 }
212 }
213
214 // Finally, add members that have not been sorted at all
215 for (Integer i : map.getNotSortableMembers()) {
216 newMembers.add(defaultMembers.get(i));
217 }
218
219 return newMembers;
220 }
221
222}
Note: See TracBrowser for help on using the repository browser.