/*
 * Copyright (c) 2009, Julian Gosnell
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.dada.demo;

import java.util.Collection;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;

import javax.swing.table.AbstractTableModel;

import org.dada.core.Metadata;
import org.dada.core.Update;
import org.dada.core.View;
import org.dada.slf4j.Logger;
import org.dada.slf4j.LoggerFactory;

public class TableModelView<K, V> extends AbstractTableModel implements View<V> {

	private final Logger logger = LoggerFactory.getLogger(getClass());
	private final CountDownLatch latch = new CountDownLatch(1);
	private Metadata<K, V> metadata;
	private final ConcurrentSkipListMap<K, V> map = new ConcurrentSkipListMap<K, V>();
	private final String name;
	
	public TableModelView(String name) {
		this.name = name;
	}
	
	public void setMetadata(Metadata<K, V> metadata) {
		this.metadata = metadata;
		latch.countDown();
	}

	public Metadata<K, V> getMetadata() {
		try {
			latch.await();
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		return metadata;
	}

	// TableModel

	// Listener

	@Override
	public void update(Collection<Update<V>> insertions, Collection<Update<V>> alterations, Collection<Update<V>> deletions) {
		logger.trace("{}:  input: insertions={}, alterations={}, deletions={}", name, insertions.size(), alterations.size(), deletions.size());

		Metadata<K, V> metadata = getMetadata();
		for (Update<V> insertion : insertions) {
			V newValue = insertion.getNewValue();
			K key = metadata.getKey(newValue);
			map.put(key, newValue);
			int index = map.headMap(key).size();
			fireTableRowsInserted(index, index);
		}
		for (Update<V> update : alterations) {
			V oldValue = update.getOldValue();
			K oldKey = metadata.getKey(oldValue);
			V newValue = update.getNewValue();
			K newKey = metadata.getKey(newValue);
			if (newKey.equals(oldKey)) {
				map.put(newKey, newValue);
				int index = map.headMap(newKey).size();
				fireTableRowsUpdated(index, index);
			} else {
				map.remove(oldKey, oldValue);
				int oldIndex = map.headMap(oldKey).size();
				fireTableRowsDeleted(oldIndex, oldIndex);
				map.put(newKey, newValue);
				int newIndex = map.headMap(newKey).size();
				fireTableRowsInserted(newIndex, newIndex);
				// TODO: if both indeces are the same...
			}
		}
		for (Update<V> deletion : deletions) {
			V value = deletion.getOldValue();
			K key = metadata.getKey(value);
			int index = map.headMap(key).size();
			map.remove(key);
			fireTableRowsDeleted(index, index);
		}
	}

	@Override
	public String getColumnName(int columnIndex) {
		return getMetadata().getAttributeKeys().get(columnIndex).toString();
	}

	@Override
	public int getColumnCount() {
		return getMetadata().getAttributeKeys().size();
	}

	@Override
	public int getRowCount() {
		return map.size();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		V value = (V) map.values().toArray()[rowIndex]; // TODO: yikes !!
		return getMetadata().getAttributeValue(value, columnIndex);
	}

}

@@ -2475,24 +2475,37 @@
 ide%0A%09public 
+synchronized 
 void update(
@@ -3389,43 +3389,62 @@
 %09%09%09%09
-map.remove(oldKey, oldValue
+int oldIndex = map.headMap(oldKey, true).size(
 );%0A%09%09%09%09i
 nt o
@@ -3435,27 +3435,27 @@
 ize();%0A%09%09%09%09i
-nt 
+f (
 oldIndex = m
@@ -3455,38 +3455,58 @@
 dex 
-= map.headMap(oldKey).size()
+%3E 0) %7B%0A%09%09%09%09%09map.remove(oldKey);%0A%09%09%09%09%09oldIndex--
 ;%0A
+%09
 %09%09%09%09
@@ -3547,24 +3547,52 @@
 Index);%0A%09%09%09%09
+%7D%0A%09%09%09%09Object currentValue = 
 map.put(newK
@@ -3657,34 +3657,121 @@
 ();%0A%09%09%09%09
-fireTableRowsInser
+if (currentValue == null)%0A%09%09%09%09%09fireTableRowsInserted(newIndex, newIndex);%0A%09%09%09%09else%0A%09%09%09%09%09fireTableRowsUpda
 ted(newI
@@ -3976,32 +3976,38 @@
  map.headMap(key
+, true
 ).size();%0A%09%09%09map
@@ -4000,16 +4000,37 @@
 size();%0A
+%09%09%09if (index %3E 0) %7B%0A%09
 %09%09%09map.r
@@ -4041,16 +4041,30 @@
 e(key);%0A
+%09%09%09%09index--;%0A%09
 %09%09%09fireT
@@ -4090,24 +4090,29 @@
 ex, index);%0A
+%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%0A%09@Ov

fix update removal bugs