// 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.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; 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; public abstract class RelationEditor extends ExtendedDialog { /** the property name for the current relation. * @see #setRelation(Relation) * @see #getRelation() */ static public final String RELATION_PROP = RelationEditor.class.getName() + ".relation"; /** the property name for the current relation snapshot * @see #getRelationSnapshot() */ static public final String RELATION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".relationSnapshot"; /** the list of registered relation editor classes */ private static ArrayList<Class<RelationEditor>> editors = new ArrayList<Class<RelationEditor>>(); /** * Registers a relation editor class. Depending on the type of relation to be edited * {@see #getEditor(OsmDataLayer, Relation, Collection)} will create an instance of * this class. * * @param clazz the class */ public void registerRelationEditor(Class<RelationEditor> clazz) { if (clazz == null) return; if (!editors.contains(clazz)) { editors.add(clazz); } } /** * The relation that this editor is working on. */ private Relation relation; /** * The version of the relation when editing is started. This is * null if a new relation is created. */ private Relation relationSnapshot; /** the data layer the relation belongs to */ private OsmDataLayer layer; /** * 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<RelationMember> selectedMembers) { for (Class<RelationEditor> e : editors) { try { Method m = e.getMethod("canEdit", Relation.class); Boolean canEdit = (Boolean) m.invoke(null, r); if (canEdit) { Constructor<RelationEditor> con = e.getConstructor(Relation.class, Collection.class); RelationEditor editor = con.newInstance(layer, r, selectedMembers); return editor; } } catch (Exception ex) { // plod on } } 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; } } /** * Creates a new relation editor * * @param layer the {@see 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. * @param selectedMembers a collection of members in <code>relation</code> which the editor * should display selected when the editor is first displayed on screen * @throws IllegalArgumentException thrown if layer is null */ protected RelationEditor(OsmDataLayer layer, Relation relation, Collection<RelationMember> selectedMembers) throws IllegalArgumentException{ // Initalizes ExtendedDialog super(Main.parent, "", new String[] { tr("Apply Changes"), tr("Cancel")}, false ); CheckParameterUtil.ensureParameterNotNull(layer, "layer"); this.layer = layer; setRelation(relation); } /** * 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())); } } /** * Replies the currently edited relation * * @return the currently edited relation */ protected Relation getRelation() { return relation; } /** * Sets the currently edited relation. Creates a snapshot of the current * state of the relation. See {@see #getRelationSnapshot()} * * @param relation the relation */ protected 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(); } /** * Replies the {@see OsmDataLayer} in whose context this relation editor is * open * * @return the {@see OsmDataLayer} in whose context this relation editor is * open */ protected OsmDataLayer getLayer() { return layer; } /** * Replies the state of the edited relation when the editor has been launched * * @return the state of the edited relation when the editor has been launched */ protected Relation getRelationSnapshot() { return relationSnapshot; } protected void setRelationSnapshot(Relation snapshot) { Relation oldValue = relationSnapshot; relationSnapshot = snapshot; if (relationSnapshot != oldValue) { support.firePropertyChange(RELATION_SNAPSHOT_PROP, oldValue, relationSnapshot); } } /** * Replies true if the currently edited relation has been changed elsewhere. * * In this case a relation editor can't apply updates to the relation directly. Rather, * it has to create a conflict. * * @return true if the currently edited relation has been changed elsewhere. */ protected boolean isDirtyRelation() { return ! relation.hasEqualSemanticAttributes(relationSnapshot); } /* ----------------------------------------------------------------------- */ /* property change support */ /* ----------------------------------------------------------------------- */ final private PropertyChangeSupport support = new PropertyChangeSupport(this); @Override public void addPropertyChangeListener(PropertyChangeListener listener) { this.support.addPropertyChangeListener(listener); } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { this.support.removePropertyChangeListener(listener); } }