/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.framework.ui.chooser;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.swing.Icon;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.uitools.Displayable;
import org.eclipse.persistence.tools.workbench.uitools.app.AbstractTreeNodeValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ListValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SimpleListValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SortedListValueModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.TreeNodeValueModel;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CompositeIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.FilteringIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator;
/**
* A package pool node holds a collection of package nodes, which each
* hold on to a collection of class description nodes, which each wrap a
* user-supplied "class description".
*
* Although the package pool node's list of package nodes may change, the
* package pool node's name is static and will never change.
*
* This node is used in the MultipleClassChooserDialog.
*/
public final class ClassDescriptionPackagePoolNode
extends AbstractTreeNodeValueModel
implements Displayable, ClassDescriptionNodeContainer
{
/** create a new dummy value with each node, or the nodes will be equal */
private Object value;
/** the package pool name - not really used, except in debugging */
private String name;
/** the package pool's packages */
private ListValueModel packageNodesHolder;
private ListValueModel sortedPackageNodesHolder;
/** cache the icons */
private Icon packageIcon;
private Icon classIcon;
/** Collator used to sort the various lists. */
private Collator collator;
// ********** constructors **********
/** construct a package pool with the specified name and user "class descriptions" */
public ClassDescriptionPackagePoolNode(String name, Iterator userClassDescriptions, ClassDescriptionAdapter adapter, WorkbenchContext context) {
this(name, userClassDescriptions, adapter, Collections.EMPTY_SET, context);
}
/** construct a package pool with the specified name and user "class descriptions" */
public ClassDescriptionPackagePoolNode(String name, Iterator userClassDescriptions, ClassDescriptionAdapter adapter, Collection excludedUserClassDescriptions, WorkbenchContext context) {
super();
this.value = new Object();
this.name = name;
this.packageIcon = context.getApplicationContext().getResourceRepository().getIcon("package");
this.classIcon = context.getApplicationContext().getResourceRepository().getIcon("class.public");
this.collator = Collator.getInstance();
this.initialize(userClassDescriptions, adapter, excludedUserClassDescriptions);
}
// ********** initialization **********
private void initialize(Iterator userClassDescriptions, ClassDescriptionAdapter adapter, Collection excludedUserClassDescriptions) {
this.packageNodesHolder = new SimpleListValueModel(new ArrayList(1000));
this.addUserClassDescriptions(userClassDescriptions, adapter, excludedUserClassDescriptions);
this.removeEmptyPackageNodes();
// wait until things are settled down to sort the packages
this.sortedPackageNodesHolder = new SortedListValueModelAdapter(this.packageNodesHolder);
}
private void addUserClassDescriptions(Iterator userClassDescriptions, ClassDescriptionAdapter adapter, Collection excludedUserClassDescriptions) {
Map packageNodeNames = new HashMap(1000);
while (userClassDescriptions.hasNext()) {
Object userClassDescription = userClassDescriptions.next();
if ( ! excludedUserClassDescriptions.contains(userClassDescription)) {
this.addClassNode(new ClassDescriptionNode(userClassDescription, adapter), packageNodeNames);
}
}
}
private void removeEmptyPackageNodes() {
// gather them up so we don't get a concurrent modification exception
List emptyPackageNodes = CollectionTools.list(this.emptyPackageNodes());
for (Iterator stream = emptyPackageNodes.iterator(); stream.hasNext(); ) {
this.removePackageNode((ClassDescriptionPackageNode) stream.next());
}
}
private Iterator emptyPackageNodes() {
return new FilteringIterator(this.packageNodes()) {
public boolean accept(Object next) {
return ((ClassDescriptionPackageNode) next).isEmpty();
}
};
}
// ********** accessors **********
ListIterator packageNodes() {
return (ListIterator) this.packageNodesHolder.getValue();
}
int packageNodesSize() {
return this.packageNodesHolder.size();
}
void addClassNode(ClassDescriptionNode classNode) {
this.addClassNode(classNode, null);
}
void addClassNode(ClassDescriptionNode classNode, Map packageNodeNames) {
this.getPackageNodeFor(classNode, packageNodeNames).addClassNode(classNode);
}
void removeClassNode(ClassDescriptionNode classNode) {
ClassDescriptionPackageNode packageNode = this.getPackageNodeFor(classNode, null);
packageNode.removeClassNode(classNode);
if (packageNode.isEmpty()) {
this.removePackageNode(packageNode);
}
}
private void removePackageNode(ClassDescriptionPackageNode packageNode) {
this.packageNodesHolder.removeItem(this.indexOfPackageNode(packageNode));
packageNode.setPoolNode(null); // ???
}
private int indexOfPackageNode(ClassDescriptionPackageNode packageNode) {
int size = this.packageNodesHolder.size();
for (int i = 0; i < size; i++) {
if (this.packageNodesHolder.getItem(i) == packageNode) {
return i;
}
}
return -1;
}
/**
* a bit of hackery here: on the first pass, the package node names map
* will be present - use it when building the first generation of package
* nodes, but it is not used when package nodes are created later
*/
private ClassDescriptionPackageNode getPackageNodeFor(ClassDescriptionNode classNode, Map packageNodeNames) {
ClassDescriptionPackageNode packageNode = null;
for (Iterator stream = this.packageNodes(); stream.hasNext(); ) {
ClassDescriptionPackageNode next = (ClassDescriptionPackageNode) stream.next();
if (classNode.belongsInPackageNode(next)) {
packageNode = next;
break;
}
}
if (packageNode == null) {
packageNode = classNode.buildPackageNode(this);
if (packageNodeNames != null) {
// if we have 2 packages with the same name, configure them to display their descriptions
ClassDescriptionPackageNode prev = (ClassDescriptionPackageNode) packageNodeNames.put(packageNode.getName(), packageNode);
if (prev != null) {
prev.setDisplaysAdditionalInfo(true);
packageNode.setDisplaysAdditionalInfo(true);
}
}
this.packageNodesHolder.addItem(this.packageNodesHolder.size(), packageNode);
}
return packageNode;
}
Icon getPackageIcon() {
return this.packageIcon;
}
Icon getClassIcon() {
return this.classIcon;
}
Collator getCollator() {
return this.collator;
}
// ********** ValueModel implementation **********
/**
* @see org.eclipse.persistence.tools.workbench.uitools.app.ValueModel#getValue()
*/
public Object getValue() {
// answer this dummy value so we aren't equal to another package pool node
return this.value;
}
// ********** TreeNodeValueModel implementation **********
/**
* @see org.eclipse.persistence.tools.workbench.uitools.app.TreeNodeValueModel#getParent()
*/
public TreeNodeValueModel getParent() {
return null;
}
/**
* @see org.eclipse.persistence.tools.workbench.uitools.app.TreeNodeValueModel#getChildrenModel()
*/
public ListValueModel getChildrenModel() {
return this.sortedPackageNodesHolder;
}
// ********** AbstractTreeNodeValueModel implementation **********
protected void engageValue() {
// the package pool is static - do nothing
}
protected void disengageValue() {
// the package pool is static - do nothing
}
// ********** ClassDescriptionNodeContainer implementation **********
/**
* @see ClassDescriptionNodeContainer#addClassNodesTo(java.util.Collection)
*/
public void addClassDescriptionNodesTo(Collection classDescriptionNodes) {
for (Iterator stream = this.packageNodes(); stream.hasNext(); ) {
((ClassDescriptionPackageNode) stream.next()).addClassDescriptionNodesTo(classDescriptionNodes);
}
}
// ********** Comparable implementation **********
public int compareTo(Object o) {
return DEFAULT_COMPARATOR.compare(this, o);
}
// ********** Displayable implementation **********
public String displayString() {
return this.name;
}
public Icon icon() {
return null;
}
// ********** queries **********
Iterator classNodes() {
return new CompositeIterator(
new TransformationIterator(this.packageNodes()) {
protected Object transform(Object next) {
return ((ClassDescriptionPackageNode) next).classNodes();
}
}
);
}
Iterator userClassDescriptions() {
return new TransformationIterator(this.classNodes()) {
protected Object transform(Object next) {
return ((ClassDescriptionNode) next).getUserClassDescription();
}
};
}
// ********** standard methods **********
public void toString(StringBuffer sb) {
sb.append(this.name);
}
}