001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.history;
003
004import java.awt.Color;
005import java.util.ArrayList;
006import java.util.Collections;
007import java.util.List;
008
009import javax.swing.table.AbstractTableModel;
010
011import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
012
013/**
014 * The table model for the tags of the version
015 * at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}
016 * or {@link PointInTimeType#CURRENT_POINT_IN_TIME}
017 * @since 11647 (extracted from HistoryBrowserModel)
018 */
019public final class TagTableModel extends AbstractTableModel {
020
021    private List<String> keys;
022    private final PointInTimeType pointInTimeType;
023    private final HistoryBrowserModel model;
024
025    /**
026     * Constructs a new {@code TagTableModel}.
027     * @param historyModel parent {@code HistoryBrowserModel}
028     * @param type type of point in time
029     */
030    public TagTableModel(HistoryBrowserModel historyModel, PointInTimeType type) {
031        model = historyModel;
032        pointInTimeType = type;
033        initKeyList();
034    }
035
036    void initKeyList() {
037        keys = new ArrayList<>(model.getKeySet());
038        Collections.sort(keys);
039        fireTableDataChanged();
040    }
041
042    @Override
043    public int getRowCount() {
044        if (keys == null)
045            return 0;
046        return keys.size();
047    }
048
049    @Override
050    public Object getValueAt(int row, int column) {
051        return getKeyAt(row);
052    }
053
054    /**
055     * Get the key for the given row.
056     * @param row The row
057     * @return The key in that row.
058     * @since 10637
059     */
060    public String getKeyAt(int row) {
061        return keys.get(row);
062    }
063
064    /**
065     * Determines if a tag exists for the given key.
066     * @param key tag key
067     * @return {@code true} if a tag exists for the given key
068     */
069    public boolean hasTag(String key) {
070        HistoryOsmPrimitive primitive = model.getPointInTime(pointInTimeType);
071        return primitive != null && primitive.hasKey(key);
072    }
073
074    /**
075     * Returns the tag value for the given key.
076     * @param key tag key
077     * @return tag value, or null
078     */
079    public String getValue(String key) {
080        HistoryOsmPrimitive primitive = model.getPointInTime(pointInTimeType);
081        if (primitive == null)
082            return null;
083        return primitive.get(key);
084    }
085
086    /**
087     * Returns the history primitive which changed the given key.
088     * @param key the OSM key
089     * @return the history primitive which changed the given key
090     */
091    public HistoryOsmPrimitive getWhichChangedTag(String key) {
092        HistoryOsmPrimitive primitive = model.getPointInTime(pointInTimeType);
093        if (primitive == null)
094            return null;
095        return model.getHistory().getWhichChangedTag(primitive, key, model.isLatest(primitive));
096    }
097
098    /**
099     * Returns a version string for the given primitive, {@code "*"} if it is {@linkplain HistoryBrowserModel#isLatest is latest}.
100     * @param primitive the history primitive
101     * @return a version string for the given primitive
102     */
103    public String getVersionString(HistoryOsmPrimitive primitive) {
104        return model.isLatest(primitive) ? "*" : "v" + primitive.getVersion();
105    }
106
107    /**
108     * Returns the color for the given primitive timestamp
109     * @param primitive the history primitive
110     * @return the color for the given primitive timestamp
111     */
112    public Color getVersionColor(HistoryOsmPrimitive primitive) {
113        return model.getVersionColor(primitive);
114    }
115
116    /**
117     * Determines if a tag exists in the opposite point in time for the given key.
118     * @param key tag key
119     * @return {@code true} if a tag exists for the given key
120     */
121    public boolean oppositeHasTag(String key) {
122        HistoryOsmPrimitive primitive = model.getPointInTime(pointInTimeType.opposite());
123        return primitive != null && primitive.hasKey(key);
124    }
125
126    /**
127     * Returns the tag value in the opposite point in time for the given key.
128     * @param key tag key
129     * @return tag value, or null
130     */
131    public String getOppositeValue(String key) {
132        HistoryOsmPrimitive primitive = model.getPointInTime(pointInTimeType.opposite());
133        if (primitive == null)
134            return null;
135        return primitive.get(key);
136    }
137
138    /**
139     * Determines if the tag value is the same in the opposite point in time for the given key.
140     * @param key tag key
141     * @return {@code true} if the tag value is the same in the opposite point in time for the given key
142     */
143    public boolean hasSameValueAsOpposite(String key) {
144        String value = getValue(key);
145        String oppositeValue = getOppositeValue(key);
146        return value != null && value.equals(oppositeValue);
147    }
148
149    /**
150     * Returns the type of point in time.
151     * @return the type of point in time
152     */
153    public PointInTimeType getPointInTimeType() {
154        return pointInTimeType;
155    }
156
157    /**
158     * Determines if this is the current point in time.
159     * @return {@code true} if this is the current point in time
160     */
161    public boolean isCurrentPointInTime() {
162        return pointInTimeType == PointInTimeType.CURRENT_POINT_IN_TIME;
163    }
164
165    /**
166     * Determines if this is the reference point in time.
167     * @return {@code true} if this is the reference point in time
168     */
169    public boolean isReferencePointInTime() {
170        return pointInTimeType == PointInTimeType.REFERENCE_POINT_IN_TIME;
171    }
172
173    @Override
174    public int getColumnCount() {
175        return 3;
176    }
177
178    TwoColumnDiff.Item.DiffItemType getDiffItemType(String key, boolean isValue) {
179        if ((!hasTag(key) && isCurrentPointInTime()) || (!oppositeHasTag(key) && isReferencePointInTime())) {
180            return TwoColumnDiff.Item.DiffItemType.DELETED;
181        } else if ((!oppositeHasTag(key) && isCurrentPointInTime()) || (!hasTag(key) && isReferencePointInTime())) {
182            return TwoColumnDiff.Item.DiffItemType.INSERTED;
183        } else if (isValue && hasTag(key) && oppositeHasTag(key) && !hasSameValueAsOpposite(key)) {
184            return TwoColumnDiff.Item.DiffItemType.CHANGED;
185        } else {
186            return TwoColumnDiff.Item.DiffItemType.EMPTY;
187        }
188    }
189}