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}