// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.czechaddress.proposal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
/**
* Stores a list of {@link ProposalContainer}s, which represent the whole
* changeset of alternations to be applied to some nodes.
*
* ProposalDatabase also impemets {@link TreeModel}, which allows an easy
* display of such object in an {@link TreeView}.
*
* @author Radomír Černoch radomir.cernoch@gmail.com
*/
public class ProposalDatabase implements TreeModel {
/**
* The internal database of {@link ProposalContainer}s.
*/
protected List<ProposalContainer> changeSet =
new ArrayList<>();
/**
* Listeners for the {@link TreeModel} interface.
*/
protected List<TreeModelListener> listeners =
new ArrayList<>();
/**
* The root element for the {@link TreeView}.
*/
protected String root = new String("Navrhované změny");
/**
* Adds a new {@link ProposalContainer} to the internal database.
*/
public void addContainer(ProposalContainer newContainer) {
assert !changeSet.contains(newContainer)
: "Containers in the database must unique.";
changeSet.add(newContainer);
}
/**
* Removes the given {@link ProposalContainer} from the internal list.
*/
public void removeContainer(ProposalContainer containerToAdd) {
changeSet.remove(containerToAdd);
}
/**
* Finds a {@link PropsalContainer} containing given {@code Primitive}.
*
* <p>If no container with the given primitive is found, null is returned.
* If there are multiple containers (which should not be the case),
* the first is returned.</p>
*
* @param primitive the primitive to be found
* @return the ProposalContainer containing primitive or null
*/
public ProposalContainer findContainer(OsmPrimitive primitive) {
for (ProposalContainer pac : changeSet) {
if (pac.getTarget().equals(primitive))
return pac;
}
return null;
}
/**
* Adds proposals corresponding to a primitive into the database.
*
* <p>If the primitive is already in the database, the proposal
* is added to its container. If not, a new container is created.</p>
*/
public void addProposals(OsmPrimitive primitive,
Collection<Proposal> proposal) {
ProposalContainer container = findContainer(primitive);
if (container == null) {
container = new ProposalContainer(primitive);
addContainer(container);
}
container.addProposals(proposal);
}
/**
* Replaces the internal changeset of {@link ProposalContainer}s
* with a new one.
*
* @param newChangeSet new changeset to replace the current one
*/
public void setContainers(ArrayList<ProposalContainer> newChangeSet) {
this.changeSet = newChangeSet;
}
/**
* Removes all {@link ProposalContainer}s from the changeset.
*/
public void clear() {
changeSet.clear();
}
/**
* Gives a reference to the internal list of {@link ProposalContainer}s.
*
* @return the refernence to internal changeset.
*/
public List<ProposalContainer> getContainers() {
return changeSet;
}
/**
* Applies all {@link Proposal}s in all {@link ProposalContainer}s.
*/
public void applyAll() {
for (ProposalContainer a : changeSet) {
a.applyAll();
}
}
//==============================================================================
// IMPLEMENTATION OF THE TREEMODEL INTERFACE
//==============================================================================
@Override
public Object getRoot() {
return root;
}
@Override
public Object getChild(Object parent, int index) {
if (parent.equals(root))
return changeSet.get(index);
if (parent instanceof ProposalContainer)
return ((ProposalContainer) parent).getProposals().get(index);
return null;
}
@Override
public int getChildCount(Object parent) {
if (parent.equals(root))
return changeSet.size();
if (parent instanceof ProposalContainer)
return ((ProposalContainer) parent).getProposals().size();
return 0;
}
@Override
public boolean isLeaf(Object node) {
if (node.equals(root))
return changeSet.size() == 0;
if (node instanceof ProposalContainer)
return ((ProposalContainer) node).getProposals().size() == 0;
return true;
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
// We are a read-only model... Nothing to do here.
}
@Override
public int getIndexOfChild(Object parent, Object child) {
if (parent.equals(root))
return changeSet.indexOf(child);
if (parent instanceof ProposalContainer)
return ((ProposalContainer) parent).getProposals().indexOf(child);
return -1;
}
@Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
/**
* Deletes a proposal at the given tree path.
*
* @param path path containing the element to be deleted
*/
public void deteleObjectAtPath(TreePath path) {
// The root element cannot be deleted
if (path.getPathCount() <= 1)
return;
// If path-length is 2, the whole ProposalContainer is deleted.
if (path.getPathCount() == 2) {
changeSet.remove(path.getPathComponent(1));
TreeModelEvent event = new TreeModelEvent(this, path);
for (TreeModelListener l : listeners) {
l.treeNodesRemoved(event);
}
return;
}
// If path-length is 3, only a single Proposal is deleted.
ProposalContainer ac = (ProposalContainer) path.getPathComponent(1);
if (path.getPathCount() == 3) {
ac.getProposals().remove(path.getPathComponent(2));
TreeModelEvent event = new TreeModelEvent(this, path);
for (TreeModelListener l : listeners) {
l.treeNodesRemoved(event);
}
return;
}
assert false : path;
return;
}
}