001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.util.Collection; 005import java.util.List; 006import java.util.stream.Collectors; 007 008import org.openstreetmap.josm.tools.Utils; 009 010/** 011 * IRelation captures the common functions of {@link Relation} and {@link RelationData}. 012 * @param <M> Type of OSM relation member 013 * @since 4098 014 */ 015public interface IRelation<M extends IRelationMember<?>> extends IPrimitive { 016 017 /** 018 * Returns the number of members. 019 * @return number of members 020 */ 021 int getMembersCount(); 022 023 /** 024 * Determines if this relation is empty, i.e. it has no members. 025 * @return {@code true} if this relation is empty, i.e. it has no members 026 * @since 16119 027 */ 028 default boolean isEmpty() { 029 return getMembersCount() == 0; 030 } 031 032 /** 033 * Returns the relation member at the specified index. 034 * @param index the index of the relation member 035 * @return relation member at the specified index 036 * @since 13766 (IRelation) 037 */ 038 M getMember(int index); 039 040 /** 041 * Returns members of the relation. 042 * @return Members of the relation. Changes made in returned list are not mapped 043 * back to the primitive, use {@link #setMembers} to modify the members 044 * @since 1925 045 * @since 13766 (IRelation) 046 */ 047 List<M> getMembers(); 048 049 /** 050 * Sets members of the relation. 051 * @param members Can be null, in that case all members are removed 052 */ 053 void setMembers(List<M> members); 054 055 /** 056 * Returns id of the member at given index. 057 * @param idx member index 058 * @return id of the member at given index 059 */ 060 long getMemberId(int idx); 061 062 /** 063 * Returns role of the member at given index. 064 * @param idx member index 065 * @return role of the member at given index 066 */ 067 String getRole(int idx); 068 069 /** 070 * Returns type of the member at given index. 071 * @param idx member index 072 * @return type of the member at given index 073 */ 074 OsmPrimitiveType getMemberType(int idx); 075 076 /** 077 * Determines if at least one child primitive is incomplete. 078 * 079 * @return true if at least one child primitive is incomplete 080 * @since 13564 081 */ 082 default boolean hasIncompleteMembers() { 083 return false; 084 } 085 086 @Override 087 default int compareTo(IPrimitive o) { 088 return o instanceof IRelation ? Long.compare(getUniqueId(), o.getUniqueId()) : -1; 089 } 090 091 @Override 092 default String getDisplayName(NameFormatter formatter) { 093 return formatter.format(this); 094 } 095 096 /** 097 * Determines if this relation is a boundary. 098 * @return {@code true} if a boundary relation 099 */ 100 default boolean isBoundary() { 101 return "boundary".equals(get("type")); 102 } 103 104 @Override 105 default boolean isMultipolygon() { 106 return "multipolygon".equals(get("type")) || isBoundary(); 107 } 108 109 /** 110 * Returns an unmodifiable list of the {@link OsmPrimitive}s referred to by at least one member of this relation. 111 * @return an unmodifiable list of the primitives 112 * @since 13957 113 */ 114 default List<? extends IPrimitive> getMemberPrimitivesList() { 115 return Utils.transform(getMembers(), IRelationMember::getMember); 116 } 117 118 /** 119 * Replies a collection with the incomplete children this relation refers to. 120 * 121 * @return the incomplete children. Empty collection if no children are incomplete. 122 * @since 13957 123 */ 124 default Collection<? extends IPrimitive> getIncompleteMembers() { 125 return getMembers().stream() 126 .filter(rm -> rm.getMember().isIncomplete()) 127 .map(rm -> rm.getMember()) 128 .collect(Collectors.toSet()); 129 } 130 131 /** 132 * Returns a list of relation members having the specified role. 133 * @param role role 134 * @return a list of relation members having the specified role 135 * @since 15418 136 */ 137 default List<? extends IPrimitive> findRelationMembers(String role) { 138 return getMembers().stream().filter(rmv -> role.equals(rmv.getRole())) 139 .map(IRelationMember::getMember).collect(Collectors.toList()); 140 } 141}