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