001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm.history;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.time.Instant;
007import java.util.ArrayList;
008import java.util.Collections;
009import java.util.List;
010import java.util.Objects;
011
012import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
013import org.openstreetmap.josm.data.osm.User;
014import org.openstreetmap.josm.data.osm.Way;
015import org.openstreetmap.josm.data.osm.WayData;
016import org.openstreetmap.josm.tools.CheckParameterUtil;
017
018/**
019 * Represents an immutable OSM way in the context of a historical view on OSM data.
020 * @since 1670
021 */
022public class HistoryWay extends HistoryOsmPrimitive {
023
024    private final List<Long> nodeIds = new ArrayList<>();
025
026    /**
027     * Constructs a new {@code HistoryWay}.
028     *
029     * @param id the id (&gt; 0 required)
030     * @param version the version (&gt; 0 required)
031     * @param visible whether the node is still visible
032     * @param user the user (!= null required)
033     * @param changesetId the changeset id (&gt; 0 required if {@code checkHistoricParams} is true)
034     * @param timestamp the timestamp (!= null required if {@code checkHistoricParams} is true)
035     * @throws IllegalArgumentException if preconditions are violated
036     */
037    public HistoryWay(long id, long version, boolean visible, User user, long changesetId, Instant timestamp) {
038        super(id, version, visible, user, changesetId, timestamp);
039    }
040
041    /**
042     * Constructs a new {@code HistoryWay} with a configurable checking of historic parameters.
043     * This is needed to build virtual HistoryWays for modified ways, which do not have a timestamp and a changeset id.
044     *
045     * @param id the id (&gt; 0 required)
046     * @param version the version (&gt; 0 required)
047     * @param visible whether the node is still visible
048     * @param user the user (!= null required)
049     * @param changesetId the changeset id (&gt; 0 required if {@code checkHistoricParams} is true)
050     * @param timestamp the timestamp (!= null required if {@code checkHistoricParams} is true)
051     * @param checkHistoricParams if true, checks values of {@code changesetId} and {@code timestamp}
052     * @throws IllegalArgumentException if preconditions are violated
053     * @since 5440
054     */
055    public HistoryWay(long id, long version, boolean visible, User user, long changesetId, Instant timestamp, boolean checkHistoricParams) {
056        super(id, version, visible, user, changesetId, timestamp, checkHistoricParams);
057    }
058
059    /**
060     * Constructs a new {@code HistoryWay} with a given list of node ids.
061     *
062     * @param id the id (&gt; 0 required)
063     * @param version the version (&gt; 0 required)
064     * @param visible whether the node is still visible
065     * @param user the user (!= null required)
066     * @param changesetId the changeset id (&gt; 0 required if {@code checkHistoricParams} is true)
067     * @param timestamp the timestamp (!= null required if {@code checkHistoricParams} is true)
068     * @param nodeIdList the node ids (!= null required)
069     * @throws IllegalArgumentException if preconditions are violated
070     */
071    public HistoryWay(long id, long version, boolean visible, User user, long changesetId, Instant timestamp, List<Long> nodeIdList) {
072        this(id, version, visible, user, changesetId, timestamp);
073        CheckParameterUtil.ensureParameterNotNull(nodeIdList, "nodeIdList");
074        this.nodeIds.addAll(nodeIdList);
075    }
076
077    /**
078     * Constructs a new {@code HistoryWay} from an existing {@link Way}.
079     * @param w the way
080     */
081    public HistoryWay(Way w) {
082        super(w);
083    }
084
085    /**
086     * replies the number of nodes in this way
087     * @return the number of nodes
088     */
089    public int getNumNodes() {
090        return nodeIds.size();
091    }
092
093    /**
094     * replies the idx-th node id in the list of node ids of this way
095     *
096     * @param idx the index
097     * @return the idx-th node id
098     * @throws IndexOutOfBoundsException if  idx &lt; 0 || idx &gt;= {#see {@link #getNumNodes()}
099     */
100    public long getNodeId(int idx) {
101        if (idx < 0 || idx >= nodeIds.size())
102            throw new IndexOutOfBoundsException(tr("Parameter {0} not in range 0..{1}. Got ''{2}''.", "idx", nodeIds.size(), idx));
103        return nodeIds.get(idx);
104    }
105
106    /**
107     * replies an immutable list of the ways node ids
108     *
109     * @return the ways node ids
110     */
111    public List<Long> getNodes() {
112        return Collections.unmodifiableList(nodeIds);
113    }
114
115    /**
116     * replies the ways type, i.e. {@link OsmPrimitiveType#WAY}
117     *
118     * @return the ways type
119     */
120    @Override
121    public OsmPrimitiveType getType() {
122        return OsmPrimitiveType.WAY;
123    }
124
125    /**
126     * adds a node id to the list nodes of this way
127     *
128     * @param ref the node id to add
129     */
130    public void addNode(long ref) {
131        nodeIds.add(ref);
132    }
133
134    /**
135     * Replies true if this way is closed.
136     *
137     * @return true if this way is closed.
138     */
139    public boolean isClosed() {
140        return getNumNodes() >= 3 && Objects.equals(nodeIds.get(0), nodeIds.get(nodeIds.size()-1));
141    }
142
143    @Override
144    public String getDisplayName(HistoryNameFormatter formatter) {
145        return formatter.format(this);
146    }
147
148    /**
149     * Fills the way attributes with values from this history.
150     * @param data way data to fill
151     * @return filled way data
152     * @since 11878
153     */
154    public WayData fillPrimitiveData(WayData data) {
155        super.fillPrimitiveCommonData(data);
156        data.setNodeIds(nodeIds);
157        return data;
158    }
159}