// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.fixAddresses.gui.actions;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.command.SequenceCommand;
import org.openstreetmap.josm.plugins.fixAddresses.AddressEditContainer;
import org.openstreetmap.josm.plugins.fixAddresses.IAddressEditContainerListener;
import org.openstreetmap.josm.plugins.fixAddresses.ICommandListener;
import org.openstreetmap.josm.plugins.fixAddresses.IOSMEntity;
import org.openstreetmap.josm.plugins.fixAddresses.StringUtils;
import org.openstreetmap.josm.plugins.fixAddresses.gui.AddressEditSelectionEvent;
/**
* Base class for all address related action. An action can work as well on all addresses collected by the
* container or on the active selection.
* By default, the action is disabled and the updateEnabledState(...) have to be implemented by
* subclasses. There are also two separate <tt>actionPerformedXX</tt> methods to do the action on
* container or on selection items.
* Most actions will work in both cases, so it is recommended to have one single method which
* accepts a list of addresses or streets and executes the tasks to be done by this action.
*
* @author Oliver Wieland <oliver.wieland@online.de>
*/
@SuppressWarnings("serial")
public abstract class AbstractAddressEditAction extends JosmAction implements IAddressEditContainerListener, ICommandListener {
private AddressEditSelectionEvent event;
protected AddressEditContainer container;
protected List<Command> commands;
private String txName;
public AbstractAddressEditAction(String name, String iconName, String tooltip, String toolbar) {
super(name, iconName, tooltip, null, true, toolbar, true);
setEnabled(false);
}
public AbstractAddressEditAction(String name) {
this(name, null, "", null);
}
/**
* Gets the current address container.
* @return the container
*/
public AddressEditContainer getContainer() {
return container;
}
/**
* @param container the container to set
*/
public void setContainer(AddressEditContainer container) {
if (container != null) { // remove old listener first
container.removeChangedListener(this);
}
this.container = container;
updateEnabledState();
if (container != null) {
container.addChangedListener(this);
}
}
/**
* @return the event
*/
protected AddressEditSelectionEvent getEvent() {
return event;
}
/**
* @param event the event to set
*/
public void setEvent(AddressEditSelectionEvent event) {
this.event = event;
updateEnabledState();
}
@Override
public void actionPerformed(ActionEvent arg0) {
if (event != null) { // use the event acquired previously.
addressEditActionPerformed(event);
event = null; // consume event
} else {
if (container != null) {
addressEditActionPerformed(container);
} else {
throw new RuntimeException("AbstractAddressEditAction has no container or event");
}
}
}
@Override
protected void updateEnabledState() {
if (this.event != null) {
updateEnabledState(this.event);
} else {
if (container != null) {
updateEnabledState(container);
} else {
super.updateEnabledState();
}
}
}
/**
* Updates 'enabled' state depending on the given address container object.
* @param container The address container (maybe null).
*/
protected abstract void updateEnabledState(AddressEditContainer container);
/**
* Updates 'enabled' state depending on the current selection.
* @param container The selection event.
*/
protected abstract void updateEnabledState(AddressEditSelectionEvent event);
/**
* Redirected action handler for doing actions on a address selection.
* @param ev selection event
*/
public abstract void addressEditActionPerformed(AddressEditSelectionEvent ev);
/**
* Redirected action handler for doing actions on an address container.
* @param container address container
*/
public abstract void addressEditActionPerformed(AddressEditContainer container);
@Override
public void containerChanged(AddressEditContainer container) {
updateEnabledState();
}
@Override
public void entityChanged(IOSMEntity node) {
container.removeProblemsOfSource(node); // clear problems of changed node...
node.visit(container, container); // .. and revisit it.
updateEnabledState();
}
/**
* Begins the transaction (command sequence). Must be called by every subclass before
* any modification on OSM objects starts.
*
* @param txName the name of the transaction (e. g. "change address tags").
*/
public void beginTransaction(String txName) {
if (commands != null && commands.size() > 0) {
throw new RuntimeException("TX has not been closed (missing finishTransaction?)");
}
commands = new ArrayList<>();
if (StringUtils.isNullOrEmpty(txName)) {
throw new RuntimeException("Transaction must have a name");
}
this.txName = txName;
}
/**
* Finishes the transaction and passes the command sequence to the framework.
*/
public void finishTransaction() {
if (commands == null) {
throw new RuntimeException("No command list available. Did you forget to call beginTransaction?");
}
// execute the command
Main.main.undoRedo.add(new SequenceCommand(txName, commands));
commands.clear();
if (container != null) {
container.invalidate();
}
}
/**
* Begins the transaction for a single object.
*
* @param entity the entity
*/
public void beginObjectTransaction(IOSMEntity entity) {
if (entity != null) {
entity.addCommandListener(this);
} else {
throw new RuntimeException("Entity must not be null");
}
}
/**
* Finishes the transaction for a single object.
*
* @param entity the entity
*/
public void finishObjectTransaction(IOSMEntity entity) {
if (entity != null) {
entity.removeCommandListener(this);
} else {
throw new RuntimeException("Entity must not be null");
}
}
@Override
public void commandIssued(IOSMEntity entity, Command command) {
if (commands == null) {
throw new RuntimeException("No command list available. Did you forget to call beginTransaction?");
}
commands.add(command);
}
}