/*******************************************************************************
* Copyright (c) 2014 Vadim Dmitriev and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Vadim Dmitriev - initial implementation
*******************************************************************************/
package org.eclipse.egit.ui.internal.rebase;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan;
import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.ElementAction;
import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.PlanElement;
import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.RebaseInteractivePlanChangeListener;
/**
* This class is used to find an index of the rebase plan entry in a list of
* non-comment rebase plan entries. It is optimized for cases in which
* {@link RebasePlanIndexer#indexOf indexOf} is invoked for plan elements in the
* same order in which these elements are stored in the rebase plan (in no
* specific direction). For example, if the last found element was at position
* 10 then the next time {@link RebasePlanIndexer#indexOf indexOf} is invoked it
* will first check element at index 10 and elements in adjacent indices: 9 and
* 11. If the element isn't found in these positions
* {@link RebasePlanIndexer#indexOf indexOf} falls back to sequential search.
*/
public class RebasePlanIndexer {
private RebaseInteractivePlan plan;
private List<PlanElement> filteredPlan;
private RebaseInteractivePlanChangeListener listener;
private int lastFoundElementPosition;
/**
* Constructs indexer for the rebase plan.
*
* @param plan
* rebase plan
*/
public RebasePlanIndexer(RebaseInteractivePlan plan) {
this.plan = plan;
this.filteredPlan = new ArrayList<>();
listener = new RebasePlanChangeListener();
plan.addRebaseInteractivePlanChangeListener(listener);
createIndex();
}
private void createIndex() {
lastFoundElementPosition = 0;
filteredPlan.clear();
for (PlanElement element : plan.getList()) {
if (!element.isComment())
filteredPlan.add(element);
}
}
/**
* Returns index of the element in the rebase plan.
*
* @param element
* rebase plan element
* @return index of the element in the rebase plan or -1 if the element is
* not found
*/
public int indexOf(Object element) {
if (filteredPlan.isEmpty())
return -1;
if (filteredPlan.get(lastFoundElementPosition).equals(element))
return lastFoundElementPosition;
int upIndex = mapToCircularIndex(lastFoundElementPosition + 1);
if (filteredPlan.get(upIndex).equals(element)) {
lastFoundElementPosition = upIndex;
return lastFoundElementPosition;
}
int downIndex = mapToCircularIndex(lastFoundElementPosition - 1);
if (filteredPlan.get(downIndex).equals(element)) {
lastFoundElementPosition = downIndex;
return lastFoundElementPosition;
}
int index = mapToCircularIndex(upIndex + 1);
while (index != downIndex) {
if (filteredPlan.get(index).equals(element)) {
lastFoundElementPosition = index;
return lastFoundElementPosition;
}
index = mapToCircularIndex(index + 1);
}
lastFoundElementPosition = 0;
return -1;
}
private int mapToCircularIndex(int index) {
int size = filteredPlan.size();
if (index < 0)
return size + index;
if (index >= size)
return index - size;
return index;
}
/**
* Disposes of this indexer.
*/
public void dispose() {
plan.removeRebaseInteractivePlanChangeListener(listener);
}
private class RebasePlanChangeListener implements RebaseInteractivePlanChangeListener {
@Override
public void planElementTypeChanged(
RebaseInteractivePlan rebaseInteractivePlan,
PlanElement element, ElementAction oldType,
ElementAction newType) {
// do nothing
}
@Override
public void planElementsOrderChanged(
RebaseInteractivePlan rebaseInteractivePlan,
PlanElement element, int oldIndex, int newIndex) {
createIndex();
}
@Override
public void planWasUpdatedFromRepository(RebaseInteractivePlan newPlan) {
createIndex();
}
}
}