/******************************************************************************* * 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"); } } }