/*******************************************************************************
* Copyright (c) 2012-2017 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.resources.tree;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.data.HasDataObject;
import org.eclipse.che.ide.api.data.tree.AbstractTreeNode;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.api.data.tree.settings.HasSettings;
import org.eclipse.che.ide.api.data.tree.settings.NodeSettings;
import org.eclipse.che.ide.api.resources.Container;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.marker.MarkerChangedEvent;
import org.eclipse.che.ide.api.resources.marker.PresentableTextMarker;
import org.eclipse.che.ide.api.resources.marker.Marker;
import org.eclipse.che.ide.api.resources.modification.CutResourceMarker;
import org.eclipse.che.ide.project.node.icon.NodeIconProvider;
import org.eclipse.che.ide.project.shared.NodesResources;
import org.eclipse.che.ide.ui.smartTree.presentation.HasPresentation;
import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation;
import org.vectomatic.dom.svg.ui.SVGResource;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayListWithExpectedSize;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static org.eclipse.che.ide.api.resources.Resource.FILE;
import static org.eclipse.che.ide.api.resources.Resource.FOLDER;
import static org.eclipse.che.ide.api.resources.Resource.PROJECT;
/**
* Abstract based implementation for all resource based nodes in the IDE.
*
* @author Vlad Zhukovskiy
* @see Resource
* @see ContainerNode
* @see FileNode
* @since 4.4.0
*/
@Beta
public abstract class ResourceNode<R extends Resource> extends AbstractTreeNode implements HasDataObject<R>,
HasPresentation,
HasSettings,
Comparable<ResourceNode> {
private static final List<Node> NO_CHILDREN = emptyList();
private R resource;
private NodeSettings nodeSettings;
private NodePresentation nodePresentation;
private boolean resourceIsCut;
private final NodeFactory nodeFactory;
protected final NodesResources nodesResources;
private final Set<NodeIconProvider> nodeIconProviders;
protected ResourceNode(R resource,
NodeSettings nodeSettings,
NodesResources nodesResources,
NodeFactory nodeFactory,
EventBus eventBus,
Set<NodeIconProvider> nodeIconProviders) {
this.resource = resource;
this.nodeSettings = nodeSettings;
this.nodeFactory = nodeFactory;
this.nodesResources = nodesResources;
this.nodeIconProviders = nodeIconProviders;
eventBus.addHandler(MarkerChangedEvent.getType(), new MarkerChangedEvent.MarkerChangedHandler() {
@Override
public void onMarkerChanged(MarkerChangedEvent event) {
if (event.getMarker().getType().equals(CutResourceMarker.ID) && getData().equals(event.getResource())) {
resourceIsCut = event.getStatus() != Marker.REMOVED;
}
}
});
}
@Override
public NodeSettings getSettings() {
return nodeSettings;
}
@Override
public R getData() {
return resource;
}
@Override
public void setData(R data) {
this.resource = data;
}
@Override
protected Promise<List<Node>> getChildrenImpl() {
checkState(getData() instanceof Container, "Not a container");
return ((Container)getData()).getChildren().then(new Function<Resource[], List<Node>>() {
@Override
public List<Node> apply(Resource[] children) throws FunctionException {
if (children == null || children.length == 0) {
return NO_CHILDREN;
}
final List<Node> nodes = newArrayListWithExpectedSize(children.length);
for (Resource child : children) {
nodes.add(createNode(child));
}
return unmodifiableList(nodes);
}
});
}
@Override
public final NodePresentation getPresentation(boolean update) {
if (nodePresentation == null) {
nodePresentation = new NodePresentation();
}
updatePresentation(nodePresentation);
return nodePresentation;
}
@Override
public void updatePresentation(@NotNull NodePresentation presentation) {
final StringBuilder cssBuilder = new StringBuilder();
final Optional<Marker> presentableTextMarker = getData().getMarker(PresentableTextMarker.ID);
if (presentableTextMarker.isPresent() && getData() instanceof Container) {
presentation.setPresentableText(((PresentableTextMarker)presentableTextMarker.get()).getPresentableText());
} else {
presentation.setPresentableText(getData().getName());
}
if (resourceIsCut) {
cssBuilder.append("opacity:0.5;");
} else {
cssBuilder.append("opacity:1;");
}
SVGResource icon = null;
for (NodeIconProvider iconProvider : nodeIconProviders) {
icon = iconProvider.getIcon(getData());
if (icon != null) {
break;
}
}
if (icon != null) {
presentation.setPresentableIcon(icon);
} else {
if (getData().getResourceType() == FOLDER) {
presentation.setPresentableIcon(getData().getName().startsWith(".") ? nodesResources.hiddenSimpleFolder()
: nodesResources.simpleFolder());
} else if (getData().getResourceType() == PROJECT) {
presentation.setPresentableIcon(((Project)getData()).isProblem() ? nodesResources.notValidProjectFolder()
: nodesResources.projectFolder());
cssBuilder.append("font-weight:bold;");
} else if (getData().getResourceType() == FILE) {
presentation.setPresentableIcon(nodesResources.file());
}
}
presentation.setPresentableTextCss(cssBuilder.toString());
}
@Override
public String getName() {
return getData().getName();
}
@Override
public boolean isLeaf() {
return getData().getResourceType() == Resource.FILE;
}
@Override
public boolean supportGoInto() {
return getData() instanceof Container;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ResourceNode)) return false;
ResourceNode<?> that = (ResourceNode<?>)o;
return Objects.equal(resource, that.resource);
}
@Override
public int hashCode() {
return Objects.hashCode(resource);
}
@Override
public int compareTo(ResourceNode o) {
return getData().compareTo(o.getData());
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("resource", resource)
.toString();
}
public interface NodeFactory {
FileNode newFileNode(File resource, NodeSettings nodeSettings);
ContainerNode newContainerNode(Container resource, NodeSettings nodeSettings);
}
protected Node createNode(Resource resource) {
checkArgument(resource != null, "Not a resource");
switch (resource.getResourceType()) {
case PROJECT:
case FOLDER:
return nodeFactory.newContainerNode((Container)resource, getSettings());
case FILE:
return nodeFactory.newFileNode((File)resource, getSettings());
default:
throw new IllegalArgumentException("Resource type was not recognized");
}
}
}