/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.gui.generic;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.cburch.logisim.file.LogisimFile;
import com.cburch.logisim.file.LibraryEvent;
import com.cburch.logisim.file.LibraryListener;
import com.cburch.logisim.tools.Library;
@SuppressWarnings("serial")
public class ProjectExplorerLibraryNode extends ProjectExplorerModel.Node<Library>
implements LibraryListener {
private LogisimFile file;
ProjectExplorerLibraryNode(ProjectExplorerModel model, Library lib) {
super(model, lib);
if (lib instanceof LogisimFile) {
file = (LogisimFile) lib;
file.addLibraryListener(this);
}
buildChildren();
}
@Override ProjectExplorerLibraryNode create(Library userObject) {
return new ProjectExplorerLibraryNode(getModel(), userObject);
}
@Override void decommission() {
if (file != null) {
file.removeLibraryListener(this);
}
for (Enumeration<?> en = children(); en.hasMoreElements(); ) {
Object n = en.nextElement();
if (n instanceof ProjectExplorerModel.Node<?>) {
((ProjectExplorerModel.Node<?>) n).decommission();
}
}
}
private void buildChildren() {
Library lib = getValue();
if (lib != null) {
buildChildren(new ProjectExplorerToolNode(getModel(), null), lib.getTools(), 0);
buildChildren(new ProjectExplorerLibraryNode(getModel(), null), lib.getLibraries(), lib.getTools().size());
}
}
private <T> void buildChildren(ProjectExplorerModel.Node<T> factory, List<? extends T> items,
int startIndex) {
// go through previously built children
Map<T, ProjectExplorerModel.Node<T>> nodeMap = new HashMap<T, ProjectExplorerModel.Node<T>>();
List<ProjectExplorerModel.Node<T>> nodeList = new ArrayList<ProjectExplorerModel.Node<T>>();
int oldPos = startIndex;
for (Enumeration<?> en = children(); en.hasMoreElements(); ) {
Object baseNode = en.nextElement();
if (baseNode.getClass() == factory.getClass()) {
@SuppressWarnings("unchecked")
ProjectExplorerModel.Node<T> node = (ProjectExplorerModel.Node<T>) baseNode;
nodeMap.put(node.getValue(), node);
nodeList.add(node);
node.oldIndex = oldPos;
node.newIndex = -1;
oldPos++;
}
}
int oldCount = oldPos;
// go through what should be the children
int actualPos = startIndex;
int insertionCount = 0;
oldPos = startIndex;
for (T tool : items) {
ProjectExplorerModel.Node<T> node = nodeMap.get(tool);
if (node == null) {
node = factory.create(tool);
node.oldIndex = -1;
node.newIndex = actualPos;
nodeList.add(node);
insertionCount++;
} else {
node.newIndex = oldPos;
oldPos++;
}
actualPos++;
}
// identify removals first
if (oldPos != oldCount) {
int[] delIndex = new int[oldCount - oldPos];
ProjectExplorerModel.Node<?>[] delNodes = new ProjectExplorerModel.Node<?>[delIndex.length];
int delPos = 0;
for (int i = nodeList.size() - 1; i >= 0; i--) {
ProjectExplorerModel.Node<T> node = nodeList.get(i);
if (node.newIndex < 0) {
node.decommission();
remove(node.oldIndex);
nodeList.remove(node.oldIndex - startIndex);
for (ProjectExplorerModel.Node<T> other : nodeList) {
if (other.oldIndex > node.oldIndex) {
other.oldIndex--;
}
}
delIndex[delPos] = node.oldIndex;
delNodes[delPos] = node;
delPos++;
}
}
this.fireNodesRemoved(delIndex, delNodes);
}
// identify moved nodes
int minChange = Integer.MAX_VALUE >> 3;
int maxChange = Integer.MIN_VALUE >> 3;
for (ProjectExplorerModel.Node<T> node : nodeList) {
if (node.newIndex != node.oldIndex && node.oldIndex >= 0) {
minChange = Math.min(minChange, node.oldIndex);
maxChange = Math.max(maxChange, node.oldIndex);
}
}
if (minChange <= maxChange) {
int[] moveIndex = new int[maxChange - minChange + 1];
ProjectExplorerModel.Node<?>[] moveNodes = new ProjectExplorerModel.Node<?>[moveIndex.length];
for (int i = maxChange; i >= minChange; i--) {
ProjectExplorerModel.Node<T> node = nodeList.get(i);
moveIndex[node.newIndex - minChange] = node.newIndex;
moveNodes[node.newIndex - minChange] = node;
remove(i);
}
for (int i = 0; i < moveIndex.length; i++) {
insert(moveNodes[i], moveIndex[i]);
}
this.fireNodesChanged(moveIndex, moveNodes);
}
// identify inserted nodes
if (insertionCount > 0) {
int[] insIndex = new int[insertionCount];
ProjectExplorerModel.Node<?>[] insNodes = new ProjectExplorerModel.Node<?>[insertionCount];
int insertionsPos = 0;
for (ProjectExplorerModel.Node<T> node : nodeList) {
if (node.oldIndex < 0) {
insert(node, node.newIndex);
insIndex[insertionsPos] = node.newIndex;
insNodes[insertionsPos] = node;
insertionsPos++;
}
}
this.fireNodesInserted(insIndex, insNodes);
}
}
@Override
public void libraryChanged(LibraryEvent event) {
switch (event.getAction()) {
case LibraryEvent.DIRTY_STATE:
case LibraryEvent.SET_NAME:
this.fireNodeChanged();
break;
case LibraryEvent.SET_MAIN:
break;
case LibraryEvent.ADD_TOOL:
case LibraryEvent.REMOVE_TOOL:
case LibraryEvent.MOVE_TOOL:
case LibraryEvent.ADD_LIBRARY:
case LibraryEvent.REMOVE_LIBRARY:
buildChildren();
break;
default:
fireStructureChanged();
}
}
}