// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.dialogs.relation; import static org.openstreetmap.josm.tools.I18n.tr; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.gui.ExtendedDialog; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.Logging; /** * Abstract relation editor. * @since 1599 */ public abstract class RelationEditor extends ExtendedDialog implements IRelationEditor { /** the property name for the current relation. * @see #setRelation(Relation) * @see #getRelation() */ public static final String RELATION_PROP = RelationEditor.class.getName() + ".relation"; /** the property name for the current relation snapshot * @see #getRelationSnapshot() */ public static final String RELATION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".relationSnapshot"; /** the list of registered relation editor classes */ private static List> editors = new ArrayList<>(); /** The relation that this editor is working on. */ private transient Relation relation; /** The version of the relation when editing is started. This is null if a new relation is created. */ private transient Relation relationSnapshot; /** The data layer the relation belongs to */ private final transient OsmDataLayer layer; private final PropertyChangeSupport support = new PropertyChangeSupport(this); /** * Creates a new relation editor * * @param layer the {@link OsmDataLayer} in whose context a relation is edited. Must not be null. * @param relation the relation. Can be null if a new relation is to be edited. * @throws IllegalArgumentException if layer is null */ protected RelationEditor(OsmDataLayer layer, Relation relation) { super(Main.parent, "", new String[] {tr("Apply Changes"), tr("Cancel")}, false, false ); CheckParameterUtil.ensureParameterNotNull(layer, "layer"); this.layer = layer; setRelation(relation); layer.removeRecentRelation(relation); } /** * Registers a relation editor class. Depending on the type of relation to be edited * {@link #getEditor(OsmDataLayer, Relation, Collection)} will create an instance of * this class. * * @param clazz the class */ public void registerRelationEditor(Class clazz) { if (clazz != null && !editors.contains(clazz)) { editors.add(clazz); } } /** * This is a factory method that creates an appropriate RelationEditor instance suitable for editing the relation * that was passed in as an argument. * * This method is guaranteed to return a working RelationEditor. If no specific editor has been registered for the * type of relation, then a generic editor will be returned. * * Editors can be registered by adding their class to the static list "editors" in the RelationEditor class. * When it comes to editing a relation, all registered editors are queried via their static "canEdit" method whether * they feel responsible for that kind of relation, and if they return true then an instance of that class will be used. * * @param layer the data layer the relation is a member of * @param r the relation to be edited * @param selectedMembers a collection of relation members which shall be selected when the editor is first launched * @return an instance of RelationEditor suitable for editing that kind of relation */ public static RelationEditor getEditor(OsmDataLayer layer, Relation r, Collection selectedMembers) { for (Class e : editors) { try { Method m = e.getMethod("canEdit", Relation.class); Boolean canEdit = (Boolean) m.invoke(null, r); if (canEdit) { return e.getConstructor(Relation.class, Collection.class).newInstance(layer, r, selectedMembers); } } catch (ReflectiveOperationException ex) { Logging.warn(ex); } } if (RelationDialogManager.getRelationDialogManager().isOpenInEditor(layer, r)) return RelationDialogManager.getRelationDialogManager().getEditorForRelation(layer, r); else { RelationEditor editor = new GenericRelationEditor(layer, r, selectedMembers); RelationDialogManager.getRelationDialogManager().positionOnScreen(editor); RelationDialogManager.getRelationDialogManager().register(layer, r, editor); return editor; } } /** * updates the title of the relation editor */ protected void updateTitle() { if (getRelation() == null) { setTitle(tr("Create new relation in layer ''{0}''", layer.getName())); } else if (getRelation().isNew()) { setTitle(tr("Edit new relation in layer ''{0}''", layer.getName())); } else { setTitle(tr("Edit relation #{0} in layer ''{1}''", relation.getId(), layer.getName())); } } @Override public final Relation getRelation() { return relation; } @Override public final void setRelation(Relation relation) { setRelationSnapshot((relation == null) ? null : new Relation(relation)); Relation oldValue = this.relation; this.relation = relation; if (this.relation != oldValue) { support.firePropertyChange(RELATION_PROP, oldValue, this.relation); } updateTitle(); } @Override public final OsmDataLayer getLayer() { return layer; } @Override public final Relation getRelationSnapshot() { return relationSnapshot; } protected final void setRelationSnapshot(Relation snapshot) { Relation oldValue = relationSnapshot; relationSnapshot = snapshot; if (relationSnapshot != oldValue) { support.firePropertyChange(RELATION_SNAPSHOT_PROP, oldValue, relationSnapshot); } } @Override public final boolean isDirtyRelation() { return !relation.hasEqualSemanticAttributes(relationSnapshot); } /* ----------------------------------------------------------------------- */ /* property change support */ /* ----------------------------------------------------------------------- */ @Override public final void addPropertyChangeListener(PropertyChangeListener listener) { this.support.addPropertyChangeListener(listener); } @Override public final void removePropertyChangeListener(PropertyChangeListener listener) { this.support.removePropertyChangeListener(listener); } @Override public void dispose() { layer.setRecentRelation(relation); super.dispose(); } }