/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.list;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.springframework.util.Assert;
import org.springframework.util.comparator.ComparableComparator;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Observable;
import java.util.Observer;
/**
* @author Mathias Broekelmann
*/
public class SortedListModel extends AbstractFilteredListModel {
private static Comparator comparableComparator = new ComparableComparator();
private Comparator comparator;
private Integer[] indexes;
private final Observer comparatorObserver = new ComparatorObserver();
public SortedListModel(ListModel model) {
this(model, comparableComparator);
}
public SortedListModel(ListModel model, Comparator comparator) {
super(model);
Assert.notNull(comparator);
this.comparator = comparator;
if (comparator instanceof Observable) {
((Observable) comparator).addObserver(comparatorObserver);
}
reallocateIndexes();
}
public void setComparator(Comparator comparator) {
Assert.notNull(comparator);
if (this.comparator instanceof Observable) {
((Observable) comparator).deleteObserver(comparatorObserver);
}
this.comparator = comparator;
if (this.comparator instanceof Observable) {
((Observable) comparator).addObserver(comparatorObserver);
}
applyComparator();
}
/**
* Internally called to reallocate the indexes. This method should be called when the filtered model changes its
* element size
*/
protected void reallocateIndexes() {
indexes = new Integer[getFilteredModel().getSize()];
for (int i = 0; i < indexes.length; i++) {
indexes[i] = new Integer(i);
}
applyComparator();
}
/**
* Returns the element index for a sorted index
*
* @param sortedIndex
* the sorted index
* @return the unsorted index of the filtered model
*/
public int getElementIndex(int sortedIndex) {
return indexes[sortedIndex].intValue();
}
protected void applyComparator() {
Integer[] indexes = new Integer[this.indexes.length];
System.arraycopy(this.indexes, 0, indexes, 0, indexes.length);
Arrays.sort(indexes, new TransformingComparator(new IndexToElementTransformer(getFilteredModel()), comparator));
this.indexes = indexes;
fireContentsChanged(this, -1, -1);
}
public void contentsChanged(ListDataEvent e) {
reallocateIndexes();
}
public void intervalAdded(ListDataEvent e) {
reallocateIndexes();
}
public void intervalRemoved(ListDataEvent e) {
reallocateIndexes();
}
private static class IndexToElementTransformer implements Transformer {
private final ListModel model;
public IndexToElementTransformer(ListModel model) {
this.model = model;
}
public Object transform(Object input) {
return model.getElementAt(((Integer) input).intValue());
}
}
private class ComparatorObserver implements Observer {
public void update(Observable o, Object arg) {
applyComparator();
}
}
}