/*
* Copyright (c) 2012, grossmann
* 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.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 jo-widgets.org 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.jowidgets.impl.base.delegate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.jowidgets.logging.api.ILogger;
import org.jowidgets.logging.api.LoggerProvider;
public final class ModelViewIndexConverter<MODEL_TYPE> {
private static final ILogger LOGGER = LoggerProvider.get(ModelViewIndexConverter.class);
private final ArrayList<MODEL_TYPE> allModels;
private final Set<MODEL_TYPE> invisibleModels;
private final ArrayList<Integer> modelToView;
public ModelViewIndexConverter() {
this.allModels = new ArrayList<MODEL_TYPE>();
this.invisibleModels = new HashSet<MODEL_TYPE>();
this.modelToView = new ArrayList<Integer>();
}
/**
* Adds a model
*
* @param model
* @param visibility
* @param modelIndex
* @return The view index where object must be added , -1 if model is not visible
*/
public int addModel(final MODEL_TYPE model, final boolean visibility, final int modelIndex) {
if (modelIndex >= 0 && modelIndex <= modelToView.size()) {
allModels.add(Integer.valueOf(modelIndex), model);
if (visibility) {
final int previousVisibleIndex = findPreviousVisibleIndex(modelIndex - 1);
modelToView.add(modelIndex, previousVisibleIndex + 1);
increaseFollowingIndices(modelIndex + 1);
return modelToView.get(modelIndex).intValue();
}
else {
invisibleModels.add(model);
modelToView.add(modelIndex, Integer.valueOf(-1));
return -1;
}
}
else {
throw new IndexOutOfBoundsException("Index must be between '0' and '" + modelToView.size() + "'");
}
}
private int findPreviousVisibleIndex(final int startIndex) {
if (startIndex != -1) {
final int fixedStartIndex = Math.min(startIndex, Math.max(0, modelToView.size() - 1));
for (int index = fixedStartIndex; index >= 0; index--) {
final int viewIndex = modelToView.get(index).intValue();
if (viewIndex != -1) {
return viewIndex;
}
}
}
return -1;
}
private void increaseFollowingIndices(final int startIndex) {
for (int index = startIndex; index < modelToView.size(); index++) {
final int viewIndex = modelToView.get(index).intValue();
if (viewIndex != -1) {
modelToView.set(index, viewIndex + 1);
}
}
}
/**
* Removes a model
*
* @param model
* @param visibility
* @param modelIndex
* @return The view index where object must be removed , -1 if model is not visible
*/
public int removeModel(final MODEL_TYPE model, final int modelIndex) {
if (modelIndex >= 0 && modelIndex < modelToView.size()) {
final boolean removed = allModels.remove(model);
if (removed) {
final int viewIndex = modelToView.get(modelIndex).intValue();
modelToView.remove(modelIndex);
if (viewIndex != -1) {
decreaseFollowingIndices(modelIndex);
}
invisibleModels.remove(model);
return viewIndex;
}
else {
return -1;
}
}
else {
throw new IndexOutOfBoundsException("Index must be between '0' and '" + (modelToView.size() - 1) + "'");
}
}
private void decreaseFollowingIndices(final int startIndex) {
for (int index = startIndex; index < modelToView.size(); index++) {
final int viewIndex = modelToView.get(index).intValue();
if (viewIndex != -1) {
modelToView.set(index, viewIndex - 1);
}
}
}
boolean isMarkedVisible(final MODEL_TYPE model) {
return !invisibleModels.contains(model);
}
/**
* Changes the visibility of the model
*
* @param model
* @param visibility
* @return The view index where object must be added / removed from the view, -1 if nothing changed
*/
public int markVisibility(final MODEL_TYPE model, final boolean visibility) {
if (isMarkedVisible(model) != visibility) {
final int modelIndex = allModels.indexOf(model);
if (modelIndex != -1) {
final Integer viewIndex = modelToView.get(modelIndex);
if (viewIndex == -1 && visibility) {
invisibleModels.remove(model);
final int previousVisibleIndex = findPreviousVisibleIndex(modelIndex - 1);
final int newViewIndex = previousVisibleIndex + 1;
modelToView.set(modelIndex, newViewIndex);
increaseFollowingIndices(modelIndex + 1);
return newViewIndex;
}
else if (viewIndex != -1 && !visibility) {
invisibleModels.add(model);
modelToView.set(modelIndex, -1);
decreaseFollowingIndices(modelIndex + 1);
return viewIndex.intValue();
}
else {
//CHECKSTYLE:OFF
LOGGER.warn("ModelViewIndexConverter seems to be inconsistent");
//CHECKSTYLE:ON
}
}
}
return -1;
}
public void clear() {
allModels.clear();
invisibleModels.clear();
modelToView.clear();
}
public void dispose() {
clear();
}
}