Ticket #17052: 17052.todo.patch

File 17052.todo.patch, 11.4 KB (added by taylor.smock, 11 months ago)

Todo changes

  • src/org/openstreetmap/josm/plugins/todo/TodoListModel.java

    Subject: [PATCH] ImportExportPlugins
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/plugins/todo/TodoListModel.java b/src/org/openstreetmap/josm/plugins/todo/TodoListModel.java
    a b  
    6060        return todoList.size();
    6161    }
    6262
     63    /**
     64     * Check if an item is done
     65     * @param todoListItem The item to look for
     66     * @return {@code true} if the item has been marked as done
     67     */
     68    boolean isDone(TodoListItem todoListItem) {
     69        return this.doneList.contains(todoListItem);
     70    }
     71
    6372    boolean isSelectionEmpty() {
    6473        return selectionModel.isSelectionEmpty();
    6574    }
  • build.xml

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/build.xml b/build.xml
    a b  
    1818        </manifest>
    1919    </target>
    2020
     21    <target name="build-jar">
     22        <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}" manifest="${manifest}" manifestencoding="UTF-8" duplicate="preserve" level="9">
     23            <service type="org.openstreetmap.josm.io.session.PluginSessionExporter" provider="org.openstreetmap.josm.plugins.todo.TodoImportExport"/>
     24            <service type="org.openstreetmap.josm.io.session.PluginSessionImporter" provider="org.openstreetmap.josm.plugins.todo.TodoImportExport"/>
     25            <restrict>
     26                <not><or>
     27                    <name name="META-INF/maven/*"/>
     28                    <name name="META-INF/DEPENDENCIES"/>
     29                    <name name="META-INF/LICENSE"/>
     30                    <name name="META-INF/NOTICE"/>
     31                    <name name="META-INF/*.RSA"/>
     32                    <name name="META-INF/*.SF"/>
     33                    <name name="module-info.class"/>
     34                </or></not>
     35                <archives>
     36                    <zips>
     37                        <fileset dir="${plugin.lib.dir}" includes="*.jar" excludes="*-sources.jar, *-javadoc.jar" erroronmissingdir="no"/>
     38                    </zips>
     39                </archives>
     40            </restrict>
     41        </jar>
     42    </target>
    2143    <!-- ** include targets that all plugins have in common ** -->
    2244    <import file="../build-common.xml"/>
    2345    <target name="revision">
  • src/org/openstreetmap/josm/plugins/todo/TodoDialog.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/plugins/todo/TodoDialog.java b/src/org/openstreetmap/josm/plugins/todo/TodoDialog.java
    a b  
    7070    private static final long serialVersionUID = 3590739974800809827L;
    7171
    7272    private final DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
    73     private final TodoListModel model = new TodoListModel(selectionModel);
     73    final TodoListModel model = new TodoListModel(selectionModel);
    7474    private final JList<TodoListItem> lstPrimitives = new JList<>(model);
    7575    private final AddAction actAdd = new AddAction(model);
    7676    private final SelectAction actSelect = new SelectAction(model);
  • new file src/org/openstreetmap/josm/plugins/todo/TodoImportExport.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/plugins/todo/TodoImportExport.java b/src/org/openstreetmap/josm/plugins/todo/TodoImportExport.java
    new file mode 100644
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.plugins.todo;
     3
     4import java.io.InputStream;
     5import java.io.OutputStream;
     6import java.util.Objects;
     7
     8import javax.json.Json;
     9import javax.json.JsonObject;
     10import javax.json.JsonValue;
     11import javax.json.stream.JsonParser;
     12
     13import org.openstreetmap.josm.data.coor.ILatLon;
     14import org.openstreetmap.josm.data.coor.LatLon;
     15import org.openstreetmap.josm.data.osm.BBox;
     16import org.openstreetmap.josm.data.osm.INode;
     17import org.openstreetmap.josm.data.osm.IRelation;
     18import org.openstreetmap.josm.data.osm.IWay;
     19import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     20import org.openstreetmap.josm.gui.MainApplication;
     21import org.openstreetmap.josm.gui.layer.AbstractOsmDataLayer;
     22import org.openstreetmap.josm.io.session.PluginSessionExporter;
     23import org.openstreetmap.josm.io.session.PluginSessionImporter;
     24
     25/**
     26 * Import and export the TODO list items
     27 */
     28public class TodoImportExport implements PluginSessionImporter, PluginSessionExporter {
     29    private record ParsedTodoListItem(TodoListItem item, boolean done){}
     30
     31    @Override
     32    public void write(OutputStream outputStream) {
     33        final var dialog = MainApplication.getMap().getToggleDialog(TodoDialog.class);
     34        try (var generator = Json.createGenerator(outputStream)) {
     35            generator.writeStartArray();
     36            for (var item : dialog.model.getTodoList()) {
     37                generator.writeStartObject();
     38                generator.write("layer", item.layer().getName());
     39                // We ought to store the geometry and tags of new items, but getting a new object based off of the centroid
     40                // should work 99% of the time. If we are storing geometry and tags of items, we will probably have
     41                // issues if someone imports a dataset and uses the TODO list plugin.
     42                if (item.primitive().isNew()) {
     43                    // We need to convert the primitive to something we can persist across sessions.
     44                    // For now, we will only store the centroid
     45                    final ILatLon centroid;
     46                    if (item.primitive() instanceof ILatLon ll) {
     47                        centroid = ll;
     48                    } else {
     49                        centroid = item.primitive().getBBox().getCenter();
     50                    }
     51                    generator.writeStartObject("centroid")
     52                            .write("lat", centroid.lat())
     53                            .write("lon", centroid.lon())
     54                            .writeEnd();
     55                }
     56                // This needs to be >=0 for the parsing section.
     57                generator.write("primitive", item.primitive().getOsmPrimitiveId().toString());
     58                if (dialog.model.isDone(item)) {
     59                    generator.write("done", true);
     60                }
     61                generator.writeEnd();
     62            }
     63            generator.writeEnd();
     64        }
     65    }
     66
     67
     68    @Override
     69    public boolean read(InputStream inputStream) {
     70        try (var parser = Json.createParser(inputStream)) {
     71            if (parser.hasNext() && parser.next() == JsonParser.Event.START_ARRAY) {
     72                final var items = parser.getArrayStream().filter(val -> val.getValueType() == JsonValue.ValueType.OBJECT)
     73                        .map(JsonValue::asJsonObject).map(TodoImportExport::parseItem).filter(Objects::nonNull)
     74                        .toList();
     75                final var model = MainApplication.getMap().getToggleDialog(TodoDialog.class).model;
     76                model.addItems(items.stream().map(ParsedTodoListItem::item).toList());
     77                model.markItems(items.stream().filter(ParsedTodoListItem::done).map(ParsedTodoListItem::item).toList());
     78                return true;
     79            }
     80        }
     81        return false;
     82    }
     83
     84    private static ParsedTodoListItem parseItem(JsonObject object) {
     85        final var layerString = object.getString("layer", null);
     86        final var primitiveIdString = object.getString("primitive", null);
     87        final var done = object.getBoolean("done", false);
     88        var primitiveId = primitiveIdString == null ? null : SimplePrimitiveId.fromString(primitiveIdString);
     89        TodoListItem item = null;
     90        if (layerString != null && primitiveId != null) {
     91            // Hopefully the user doesn't have two layers with the same primitive.
     92            // If this happens often, we may need to fiddle with the save code.
     93            final var layers = MainApplication.getLayerManager().getLayersOfType(AbstractOsmDataLayer.class)
     94                    .stream().filter(layer -> layerString.equals(layer.getName())).toList();
     95            for (var layer : layers) {
     96                final var primitive = layer.getDataSet().getPrimitiveById(primitiveId);
     97                if (primitive != null) {
     98                    item = new TodoListItem(layer, primitive);
     99                }
     100            }
     101            if (item == null && object.containsKey("centroid")) {
     102                final var centroid = object.getJsonObject("centroid");
     103                final ILatLon ll = new LatLon(centroid.getJsonNumber("lat").doubleValue(), centroid.getJsonNumber("lon").doubleValue());
     104                for (var layer : layers) {
     105                    final var bbox = new BBox(ll);
     106                    final var prim = switch (primitiveId.getType()) {
     107                        case NODE -> {
     108                            for (INode node : layer.getDataSet().searchNodes(bbox)) {
     109                                if (node.isNew() && node.equalsEpsilon(ll)) {
     110                                    yield node;
     111                                }
     112                            }
     113                            yield null;
     114                        }
     115                        case CLOSEDWAY, WAY -> {
     116                            for (IWay<?> way : layer.getDataSet().searchWays(bbox)) {
     117                                if (way.isNew() && way.getBBox().getCenter().equalsEpsilon(ll)) {
     118                                    yield way;
     119                                }
     120                            }
     121                            yield null;
     122                        }
     123                        case MULTIPOLYGON, RELATION -> {
     124                            for (IRelation<?> relation : layer.getDataSet().searchRelations(bbox)) {
     125                                if (relation.isNew() && relation.getBBox().getCenter().equalsEpsilon(ll)) {
     126                                    yield relation;
     127                                }
     128                            }
     129                            yield null;
     130                        }
     131                    };
     132                    if (prim != null) {
     133                        item = new TodoListItem(layer, prim);
     134                    }
     135                }
     136            }
     137        }
     138        if (item == null) {
     139            return null;
     140        }
     141        return new ParsedTodoListItem(item, done);
     142    }
     143
     144    @Override
     145    public boolean requiresSaving() {
     146        final var todo =  MainApplication.getMap().getToggleDialog(TodoDialog.class);
     147        return !todo.getItems().isEmpty();
     148    }
     149
     150    @Override
     151    public String getFileName() {
     152        return "todo.json";
     153    }
     154}