Ignore:
Timestamp:
2020-11-25T11:50:22+01:00 (4 years ago)
Author:
GerdP
Message:

see #19885: memory leak with "temporary" objects in validator and actions

  • (hopefully) fix memory leaks in complex actions
  • handle complex cases with presets and RelationEditor

I hope these changes don't break plugins which extend or overwrite RelationEditor

Location:
trunk/src/org/openstreetmap/josm/command
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java

    r16800 r17358  
    88import java.util.Collection;
    99import java.util.Collections;
     10import java.util.HashMap;
    1011import java.util.LinkedList;
    1112import java.util.List;
    1213import java.util.Map;
     14import java.util.Map.Entry;
    1315import java.util.NoSuchElementException;
    1416import java.util.Objects;
     
    2123import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2224import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     25import org.openstreetmap.josm.data.osm.Tagged;
    2326import org.openstreetmap.josm.tools.I18n;
    2427import org.openstreetmap.josm.tools.ImageProvider;
     
    7376     * @param ds The target data set. Must not be {@code null}
    7477     * @param objects the objects to modify. Must not be empty
    75      * @param tags the tags to set
     78     * @param tags the tags to set. Caller must make sure that the tas are not changed once the command was executed.
    7679     * @since 12726
    7780     */
     
    8689     *
    8790     * @param objects the objects to modify. Must not be empty, and objects must belong to a data set
    88      * @param tags the tags to set
     91     * @param tags the tags to set. Caller must make sure that the tas are not changed once the command was executed.
    8992     * @throws NullPointerException if objects is null or contain null item
    9093     * @throws NoSuchElementException if objects is empty
     
    287290                Objects.equals(tags, that.tags);
    288291    }
     292
     293    /**
     294     * Calculate the {@link ChangePropertyCommand} that is needed to change the tags in source to be equal to those in target.
     295     * @param source the source primitive
     296     * @param target the target primitive
     297     * @return null if no changes are needed, else a {@link ChangePropertyCommand}
     298     * @since 17357
     299     */
     300    public static Command build(OsmPrimitive source, Tagged target) {
     301        Map<String, String> changedTags = new HashMap<>();
     302        // find tags which have to be changed or removed
     303        for (Entry<String, String> tag : source.getKeys().entrySet()) {
     304            String key = tag.getKey();
     305            String val = target.get(key);
     306            if (!tag.getValue().equals(val))
     307                changedTags.put(key, val); // null or a different value
     308        }
     309        // find tags which exist only in target, they have to be added
     310        for (Entry<String, String> tag : target.getKeys().entrySet()) {
     311            String key = tag.getKey();
     312            if (!source.hasTag(key))
     313                changedTags.put(key, tag.getValue());
     314        }
     315        if (changedTags.isEmpty())
     316            return null;
     317        if (changedTags.size() == 1) {
     318            Entry<String, String> tag = changedTags.entrySet().iterator().next();
     319            return new ChangePropertyCommand(Collections.singleton(source), tag.getKey(), tag.getValue());
     320        }
     321        return new ChangePropertyCommand(Collections.singleton(source), new HashMap<>(changedTags));
     322    }
    289323}
  • trunk/src/org/openstreetmap/josm/command/SplitWayCommand.java

    r17163 r17358  
    1717import java.util.HashSet;
    1818import java.util.Iterator;
     19import java.util.LinkedHashSet;
    1920import java.util.LinkedList;
    2021import java.util.List;
     
    9091     * @param newSelection The new list of selected primitives ids (which is saved for later retrieval with {@link #getNewSelection})
    9192     * @param originalWay The original way being split (which is saved for later retrieval with {@link #getOriginalWay})
    92      * @param newWays The resulting new ways (which is saved for later retrieval with {@link #getOriginalWay})
     93     * @param newWays The resulting new ways (which is saved for later retrieval with {@link #getNewWays})
    9394     */
    9495    public SplitWayCommand(String name, Collection<Command> commandList,
     
    402403                } catch (OsmTransferException e) {
    403404                    ExceptionDialogUtil.explainException(e);
     405                    analysis.cleanup();
    404406                    return Optional.empty();
    405407                }
    406408                // If missing relation members were downloaded, perform the analysis again to find the relation
    407409                // member order for all relations.
     410                analysis.cleanup();
    408411                analysis = analyseSplit(way, wayToKeep, newWays);
    409                 return Optional.of(splitBasedOnAnalyses(way, newWays, newSelection, analysis, indexOfWayToKeep));
     412                break;
    410413            case GO_AHEAD_WITHOUT_DOWNLOADS:
    411414                // Proceed with the split with the information we have.
    412415                // This can mean that there are no missing members we want, or that the user chooses to continue
    413416                // the split without downloading them.
    414                 return Optional.of(splitBasedOnAnalyses(way, newWays, newSelection, analysis, indexOfWayToKeep));
     417                break;
    415418            case USER_ABORTED:
    416419            default:
    417420                return Optional.empty();
     421        }
     422        try {
     423            return Optional.of(splitBasedOnAnalyses(way, newWays, newSelection, analysis, indexOfWayToKeep));
     424        } finally {
     425            // see #19885
     426            wayToKeep.setNodes(null);
     427            analysis.cleanup();
    418428        }
    419429    }
     
    465475                    }
    466476                    if (c == null) {
    467                         c = new Relation(r);
     477                        c = new Relation(r); // #19885: will be removed later
    468478                    }
    469479
     
    552562                }
    553563            }
    554 
    555             if (c != null) {
    556                 commandList.add(new ChangeCommand(r.getDataSet(), r, c));
    557             }
    558564        }
    559565        changedWay.setNodes(null); // see #19885
     
    575581            warningTypes = warnings;
    576582            this.numberOfRelations = numberOfRelations;
     583        }
     584
     585        /**
     586         * Unlink temporary copies of relations. See #19885
     587         */
     588        void cleanup() {
     589            for (RelationAnalysis ra : relationAnalyses) {
     590                if (ra.relation.getDataSet() == null)
     591                    ra.relation.setMembers(null);
     592            }
    577593        }
    578594
     
    688704        }
    689705
     706        Set<Relation> modifiedRelations = new LinkedHashSet<>();
    690707        // Perform the split.
    691708        for (RelationAnalysis relationAnalysis : analysis.getRelationAnalyses()) {
     
    729746                }
    730747            }
     748            modifiedRelations.add(relation);
     749        }
     750        for (Relation r : modifiedRelations) {
     751            DataSet ds = way.getDataSet();
     752            Relation orig = (Relation) ds.getPrimitiveById(r);
     753            analysis.getCommands().add(new ChangeMembersCommand(orig, new ArrayList<>(r.getMembers())));
     754            r.setMembers(null); // see #19885
    731755        }
    732756
Note: See TracChangeset for help on using the changeset viewer.