/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.projecttree.nodes;
import org.eclipse.che.api.project.gwt.client.ProjectServiceClient;
import org.eclipse.che.api.project.shared.dto.ItemReference;
import org.eclipse.che.ide.api.project.tree.AbstractTreeNode;
import org.eclipse.che.ide.api.project.tree.TreeNode;
import org.eclipse.che.ide.api.project.tree.generic.FileNode;
import org.eclipse.che.ide.api.project.tree.generic.FolderNode;
import org.eclipse.che.ide.collections.Array;
import org.eclipse.che.ide.collections.Collections;
import org.eclipse.che.ide.ext.java.client.projecttree.JavaTreeStructure;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.util.loging.Log;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.web.bindery.event.shared.EventBus;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Comparator;
/**
* Abstract base class for all nodes that represent a container for Java source files and packages.
* There are exactly two kinds of this container: {@link PackageNode}, {@link SourceFolderNode}.
* <p/>
* It may recognize 'empty' child packages as one 'compacted' package (e.g. org.eclipse.che.ide).
* A package is considered 'empty' if it has only one child package.
*
* @author Artem Zatsarynnyy
* @see SourceFolderNode
* @see PackageNode
*/
public abstract class AbstractSourceContainerNode extends FolderNode {
protected static final Comparator<TreeNode> NODE_COMPARATOR = new NodeComparator();
public AbstractSourceContainerNode(TreeNode<?> parent,
ItemReference data,
JavaTreeStructure treeStructure,
EventBus eventBus,
ProjectServiceClient projectServiceClient,
DtoUnmarshallerFactory dtoUnmarshallerFactory) {
super(parent, data, treeStructure, eventBus, projectServiceClient, dtoUnmarshallerFactory);
}
@Nonnull
@Override
public JavaTreeStructure getTreeStructure() {
return (JavaTreeStructure)super.getTreeStructure();
}
@Override
public void refreshChildren(final AsyncCallback<TreeNode<?>> callback) {
if (!getTreeStructure().getSettings().isCompactEmptyPackages()) {
// refresh children as simple folder
super.refreshChildren(callback);
} else {
getChildren(getData().getPath(), new AsyncCallback<Array<ItemReference>>() {
@Override
public void onSuccess(Array<ItemReference> childItems) {
getChildNodesForItems(childItems, new AsyncCallback<Array<TreeNode<?>>>() {
@Override
public void onSuccess(Array<TreeNode<?>> result) {
setChildren(result);
callback.onSuccess(AbstractSourceContainerNode.this);
}
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
});
}
@Override
public void onFailure(Throwable exception) {
callback.onFailure(exception);
}
});
}
}
private void getChildNodesForItems(final Array<ItemReference> childItems, final AsyncCallback<Array<TreeNode<?>>> callback) {
final Array<TreeNode<?>> oldChildren = Collections.createArray(getChildren().asIterable());
final Array<TreeNode<?>> newChildren = Collections.createArray();
if (childItems.isEmpty()) {
callback.onSuccess(newChildren);
return;
}
final int[] asyncCounter = new int[1];
for (final ItemReference item : childItems.asIterable()) {
getCompactedPackageItemReference(item, new AsyncCallback<ItemReference>() {
@Override
public void onSuccess(ItemReference fileItemOrCompactedPackageItem) {
asyncCounter[0]++;
final AbstractTreeNode node = createChildNode(fileItemOrCompactedPackageItem);
if (node != null) {
if (oldChildren.contains(node)) {
final int i = oldChildren.indexOf(node);
newChildren.add(oldChildren.get(i));
} else {
newChildren.add(node);
}
}
if (childItems.size() == asyncCounter[0]) {
newChildren.sort(NODE_COMPARATOR);
callback.onSuccess(newChildren);
}
}
@Override
public void onFailure(Throwable caught) {
Log.error(AbstractSourceContainerNode.class, caught);
}
});
}
}
private void getCompactedPackageItemReference(final ItemReference item, final AsyncCallback<ItemReference> callback) {
if (!"folder".equals(item.getType())) {
callback.onSuccess(item);
} else {
getChildren(item.getPath(), new AsyncCallback<Array<ItemReference>>() {
@Override
public void onSuccess(Array<ItemReference> children) {
if (children.size() == 1 && "folder".equals(children.get(0).getType())) {
final ItemReference emptyPackageItem = children.get(0);
getCompactedPackageItemReference(emptyPackageItem, callback);
} else {
callback.onSuccess(item);
}
}
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
});
}
}
@Nullable
@Override
protected AbstractTreeNode<?> createChildNode(ItemReference item) {
if ("file".equals(item.getType()) && item.getName().endsWith(".java")) {
return getTreeStructure().newSourceFileNode(this, item);
} else if ("folder".equals(item.getType())) {
return getTreeStructure().newPackageNode(this, item);
} else {
return super.createChildNode(item);
}
}
@Override
public boolean canContainsFolder() {
return false;
}
private static class NodeComparator implements Comparator<TreeNode> {
@Override
public int compare(TreeNode o1, TreeNode o2) {
if (o1 instanceof FolderNode && o2 instanceof FileNode) {
return -1;
}
if (o1 instanceof FileNode && o2 instanceof FolderNode) {
return 1;
}
return o1.getDisplayName().compareTo(o2.getDisplayName());
}
}
}