001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyChangeSupport;
006import java.io.File;
007import java.util.ArrayList;
008import java.util.Collections;
009import java.util.Comparator;
010import java.util.List;
011import java.util.stream.Collectors;
012
013import javax.swing.table.DefaultTableModel;
014
015import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
016import org.openstreetmap.josm.gui.layer.OsmDataLayer;
017
018/**
019 * Table model for the {@link SaveLayersTable} in the {@link SaveLayersDialog}.
020 */
021public class SaveLayersModel extends DefaultTableModel {
022    public static final String MODE_PROP = SaveLayerInfo.class.getName() + ".mode";
023
024    public enum Mode {
025        EDITING_DATA,
026        UPLOADING_AND_SAVING
027    }
028
029    private transient List<SaveLayerInfo> layerInfo;
030    private Mode mode;
031    private final PropertyChangeSupport support;
032
033    // keep in sync with how the columns are ordered in SaveLayersTableColumnModel#build
034    private static final int columnFilename = 0;
035    private static final int columnActions = 2;
036
037    /**
038     * Constructs a new {@code SaveLayersModel}.
039     */
040    public SaveLayersModel() {
041        mode = Mode.EDITING_DATA;
042        support = new PropertyChangeSupport(this);
043    }
044
045    public void addPropertyChangeListener(PropertyChangeListener l) {
046        support.addPropertyChangeListener(l);
047    }
048
049    public void removePropertyChangeListener(PropertyChangeListener l) {
050        support.removePropertyChangeListener(l);
051    }
052
053    protected void fireModeChanged(Mode oldValue, Mode newValue) {
054        support.firePropertyChange(MODE_PROP, oldValue, newValue);
055    }
056
057    public void setMode(Mode newValue) {
058        Mode oldValue = this.mode;
059        this.mode = newValue;
060        fireModeChanged(oldValue, newValue);
061    }
062
063    public Mode getMode() {
064        return mode;
065    }
066
067    /**
068     * Populates the model with given modifiable layers.
069     * @param layers The layers to use to populate this model
070     * @since 7358
071     */
072    public void populate(List<? extends AbstractModifiableLayer> layers) {
073        layerInfo = new ArrayList<>();
074        if (layers == null) return;
075        for (AbstractModifiableLayer layer: layers) {
076            layerInfo.add(new SaveLayerInfo(layer));
077        }
078        layerInfo.sort(Comparator.naturalOrder());
079        fireTableDataChanged();
080    }
081
082    @Override
083    public int getRowCount() {
084        if (layerInfo == null) return 0;
085        return layerInfo.size();
086    }
087
088    @Override
089    public Object getValueAt(int row, int column) {
090        if (layerInfo == null) return null;
091        return layerInfo.get(row);
092    }
093
094    @Override
095    public boolean isCellEditable(int row, int column) {
096        return column == columnFilename || column == columnActions;
097    }
098
099    @Override
100    public void setValueAt(Object value, int row, int column) {
101        final SaveLayerInfo info = this.layerInfo.get(row);
102        switch(column) {
103        case columnFilename:
104            info.setFile((File) value);
105            if (info.isSavable()) {
106                info.setDoSaveToFile(true);
107            }
108            break;
109        case columnActions:
110            boolean[] values = (boolean[]) value;
111            info.setDoUploadToServer(values[0]);
112            info.setDoSaveToFile(values[1]);
113            break;
114        default: // Do nothing
115        }
116        fireTableDataChanged();
117    }
118
119    public List<SaveLayerInfo> getSafeLayerInfo() {
120        return this.layerInfo;
121    }
122
123    public List<SaveLayerInfo> getLayersWithoutFilesAndSaveRequest() {
124        if (layerInfo == null) {
125            return Collections.emptyList();
126        }
127        return layerInfo.stream().filter(info -> info.isDoSaveToFile() && info.getFile() == null).collect(Collectors.toList());
128    }
129
130    public List<SaveLayerInfo> getLayersWithIllegalFilesAndSaveRequest() {
131        if (layerInfo == null) {
132            return Collections.emptyList();
133        }
134        return layerInfo.stream()
135                .filter(info -> info.isDoSaveToFile() && info.getFile() != null && info.getFile().exists() && !info.getFile().canWrite())
136                .collect(Collectors.toList());
137    }
138
139    public List<SaveLayerInfo> getLayersWithConflictsAndUploadRequest() {
140        List<SaveLayerInfo> ret = new ArrayList<>();
141        if (layerInfo != null) {
142            for (SaveLayerInfo info: layerInfo) {
143                AbstractModifiableLayer l = info.getLayer();
144                if (info.isDoUploadToServer() && l instanceof OsmDataLayer && !((OsmDataLayer) l).getConflicts().isEmpty()) {
145                    ret.add(info);
146                }
147            }
148        }
149        return ret;
150    }
151
152    public List<SaveLayerInfo> getLayersToUpload() {
153        if (layerInfo == null) {
154            return Collections.emptyList();
155        }
156        return layerInfo.stream().filter(SaveLayerInfo::isDoUploadToServer).collect(Collectors.toList());
157    }
158
159    public List<SaveLayerInfo> getLayersToSave() {
160        if (layerInfo == null) {
161            return Collections.emptyList();
162        }
163        return layerInfo.stream().filter(SaveLayerInfo::isDoSaveToFile).collect(Collectors.toList());
164    }
165
166    public void setUploadState(AbstractModifiableLayer layer, UploadOrSaveState state) {
167        SaveLayerInfo info = getSaveLayerInfo(layer);
168        if (info != null) {
169            info.setUploadState(state);
170        }
171        fireTableDataChanged();
172    }
173
174    public void setSaveState(AbstractModifiableLayer layer, UploadOrSaveState state) {
175        SaveLayerInfo info = getSaveLayerInfo(layer);
176        if (info != null) {
177            info.setSaveState(state);
178        }
179        fireTableDataChanged();
180    }
181
182    public SaveLayerInfo getSaveLayerInfo(AbstractModifiableLayer layer) {
183        return this.layerInfo.stream()
184                .filter(info -> info.getLayer() == layer)
185                .findFirst().orElse(null);
186    }
187
188    public void resetSaveAndUploadState() {
189        for (SaveLayerInfo info: layerInfo) {
190            info.setSaveState(null);
191            info.setUploadState(null);
192        }
193    }
194
195    public boolean hasUnsavedData() {
196        for (SaveLayerInfo info: layerInfo) {
197            if (info.isDoUploadToServer() && UploadOrSaveState.OK != info.getUploadState())
198                return true;
199            if (info.isDoSaveToFile() && UploadOrSaveState.OK != info.getSaveState())
200                return true;
201        }
202        return false;
203    }
204
205    public int getNumCancel() {
206        return (int) layerInfo.stream()
207                .filter(info -> UploadOrSaveState.CANCELED == info.getSaveState() || UploadOrSaveState.CANCELED == info.getUploadState())
208                .count();
209    }
210
211    public int getNumFailed() {
212        return (int) layerInfo.stream()
213                .filter(info -> UploadOrSaveState.FAILED == info.getSaveState() || UploadOrSaveState.FAILED == info.getUploadState())
214                .count();
215    }
216}