/******************************************************************************* * Copyright (c) 2012-2016 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.project.node; import com.google.common.base.Strings; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.project.gwt.client.ProjectServiceClient; import org.eclipse.che.api.project.shared.dto.ItemReference; 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.api.promises.client.callback.AsyncPromiseHelper; import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.RequestCall; import org.eclipse.che.api.promises.client.js.Promises; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.project.node.HasProjectConfig; import org.eclipse.che.ide.api.project.node.Node; import org.eclipse.che.ide.api.project.node.settings.NodeSettings; import org.eclipse.che.ide.api.project.node.settings.SettingsProvider; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.java.client.JavaResources; import org.eclipse.che.ide.ext.java.client.navigation.service.JavaNavigationService; import org.eclipse.che.ide.ext.java.client.project.node.jar.JarContainerNode; import org.eclipse.che.ide.ext.java.client.project.settings.JavaNodeSettings; import org.eclipse.che.ide.ext.java.client.project.settings.JavaNodeSettingsProvider; import org.eclipse.che.ide.ext.java.shared.Constants; import org.eclipse.che.ide.ext.java.shared.Jar; import org.eclipse.che.ide.ext.java.shared.JarEntry; import org.eclipse.che.ide.project.node.NodeManager; import org.eclipse.che.ide.project.node.factory.NodeFactory; import org.eclipse.che.ide.project.node.icon.NodeIconProvider; import org.eclipse.che.ide.project.shared.NodesResources; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.Unmarshallable; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; /** * @author Vlad Zhukovskiy */ @Singleton public class JavaNodeManager extends NodeManager { private JavaNavigationService javaService; private JavaNodeFactory javaNodeFactory; private JavaResources javaResources; private EventBus eventBus; private JavaNodeSettingsProvider settingsProvider; public static final String JAVA_MIME_TYPE = "text/x-java-source"; public static final String JAVA_EXT = ".java"; @Inject public JavaNodeManager(NodeFactory nodeFactory, ProjectServiceClient projectServiceClient, DtoUnmarshallerFactory dtoUnmarshaller, NodesResources nodesResources, SettingsProvider nodeSettingsProvider, DtoFactory dtoFactory, JavaNavigationService javaService, JavaNodeFactory javaNodeFactory, Map<String, SettingsProvider> settingsProviderMap, JavaResources javaResources, EventBus eventBus, Set<NodeIconProvider> nodeIconProvider, AppContext appContext) { super(nodeFactory, projectServiceClient, dtoUnmarshaller, nodesResources, nodeSettingsProvider, dtoFactory, nodeIconProvider, appContext); this.javaService = javaService; this.javaNodeFactory = javaNodeFactory; this.javaResources = javaResources; this.eventBus = eventBus; if (!(settingsProviderMap.containsKey("java") || settingsProviderMap.get("java") instanceof JavaNodeSettingsProvider)) { throw new IllegalStateException("Java node settings provider was not found"); } this.settingsProvider = (JavaNodeSettingsProvider)settingsProviderMap.get("java"); } /** ******** External Libraries operations ********************* */ @NotNull public Promise<List<Node>> getExternalLibraries(ProjectConfigDto projectConfig) { return AsyncPromiseHelper.createFromAsyncRequest(getExternalLibrariesRC(projectConfig.getPath())) .then(createJarNodes(projectConfig, settingsProvider.getSettings())); } @NotNull private RequestCall<List<Jar>> getExternalLibrariesRC(@NotNull final String projectPath) { return new RequestCall<List<Jar>>() { @Override public void makeCall(AsyncCallback<List<Jar>> callback) { javaService.getExternalLibraries(projectPath, _callback(callback, dtoUnmarshaller.newListUnmarshaller(Jar.class))); } }; } @NotNull private Function<List<Jar>, List<Node>> createJarNodes(@NotNull final ProjectConfigDto projectConfig, @NotNull final NodeSettings nodeSettings) { return new Function<List<Jar>, List<Node>>() { @Override public List<Node> apply(List<Jar> jars) throws FunctionException { List<Node> nodes = new ArrayList<>(jars.size()); for (Jar jar : jars) { JarContainerNode jarContainerNode = javaNodeFactory.newJarContainerNode(jar, projectConfig, nodeSettings); nodes.add(jarContainerNode); } return nodes; } }; } /** ******** Jar Library Children operations ********************* */ @NotNull public Promise<List<Node>> getJarLibraryChildren(ProjectConfigDto projectConfig, int libId, @NotNull NodeSettings nodeSettings) { return AsyncPromiseHelper.createFromAsyncRequest(getLibraryChildrenRC(projectConfig.getPath(), libId)) .then(createJarEntryNodes(libId, projectConfig, nodeSettings)); } @NotNull private RequestCall<List<JarEntry>> getLibraryChildrenRC(@NotNull final String projectPath, final int libId) { return new RequestCall<List<JarEntry>>() { @Override public void makeCall(AsyncCallback<List<JarEntry>> callback) { javaService .getLibraryChildren(projectPath, libId, _callback(callback, dtoUnmarshaller.newListUnmarshaller(JarEntry.class))); } }; } @NotNull public Promise<List<Node>> getJarChildren(ProjectConfigDto projectConfig, int libId, @NotNull String path, @NotNull NodeSettings nodeSettings) { return AsyncPromiseHelper.createFromAsyncRequest(getChildrenRC(projectConfig.getPath(), libId, path)) .then(createJarEntryNodes(libId, projectConfig, nodeSettings)); } @NotNull private RequestCall<List<JarEntry>> getChildrenRC(@NotNull final String projectPath, final int libId, @NotNull final String path) { return new RequestCall<List<JarEntry>>() { @Override public void makeCall(AsyncCallback<List<JarEntry>> callback) { javaService .getChildren(projectPath, libId, path, _callback(callback, dtoUnmarshaller.newListUnmarshaller(JarEntry.class))); } }; } @NotNull private Function<List<JarEntry>, List<Node>> createJarEntryNodes(final int libId, final ProjectConfigDto projectConfig, final NodeSettings nodeSettings) { return new Function<List<JarEntry>, List<Node>>() { @Override public List<Node> apply(List<JarEntry> entries) throws FunctionException { List<Node> nodes = new ArrayList<>(); for (JarEntry jarEntry : entries) { Node node = createNode(jarEntry, libId, projectConfig, nodeSettings); if (node != null) { nodes.add(node); } } return nodes; } }; } private Node createNode(JarEntry entry, int id, ProjectConfigDto projectConfig, NodeSettings nodeSettings) { if (entry.getType() == JarEntry.JarEntryType.FOLDER || entry.getType() == JarEntry.JarEntryType.PACKAGE) { return javaNodeFactory.newJarFolderNode(entry, id, projectConfig, nodeSettings); } else if (entry.getType() == JarEntry.JarEntryType.FILE || entry.getType() == JarEntry.JarEntryType.CLASS_FILE) { return javaNodeFactory.newJarFileNode(entry, id, projectConfig, nodeSettings); } return null; } /** ******** Common methods ********************* */ public static boolean isJavaProject(Node node) { if (!(node instanceof HasProjectConfig)) { return false; } ProjectConfigDto descriptor = ((HasProjectConfig)node).getProjectConfig(); Map<String, List<String>> attributes = descriptor.getAttributes(); return attributes.containsKey(Constants.LANGUAGE) && attributes.get(Constants.LANGUAGE) != null && "java".equals(attributes.get(Constants.LANGUAGE).get(0)); } public JavaResources getJavaNodesResources() { return javaResources; } public JavaNodeFactory getJavaNodeFactory() { return javaNodeFactory; } public JavaNodeSettingsProvider getJavaSettingsProvider() { return settingsProvider; } @Override public Node createNodeByType(ItemReference itemReference, ProjectConfigDto projectConfig, NodeSettings settings) { if ("folder".equals(itemReference.getType()) || "project".equals(itemReference.getType())) { return javaNodeFactory.newPackageNode(itemReference, projectConfig, (JavaNodeSettings)settingsProvider.getSettings()); } else if ("file".equals(itemReference.getType())) { return createFileNodeByType(itemReference, projectConfig, settings); } return null; } public Node createFileNodeByType(ItemReference itemReference, ProjectConfigDto projectConfig, NodeSettings settings) { if (isJavaItemReference(itemReference)) { return javaNodeFactory.newJavaFileNode(itemReference, projectConfig, (JavaNodeSettings)settingsProvider.getSettings()); } return nodeFactory.newFileReferenceNode(itemReference, projectConfig, settings); } public boolean isJavaItemReference(ItemReference itemReference) { final String mimeType = itemReference.getMediaType(); //first detect by mime type if (!Strings.isNullOrEmpty(mimeType) && JAVA_MIME_TYPE.equals(mimeType)) { return true; } return itemReference.getName().endsWith(JAVA_EXT); } public EventBus getEventBus() { return eventBus; } public JavaNavigationService getJavaService() { return javaService; } public Promise<Node> getClassNode(final ProjectConfigDto projectConfig, final int libId, final String path) { return AsyncPromiseHelper.createFromAsyncRequest(new RequestCall<Node>() { @Override public void makeCall(final AsyncCallback<Node> callback) { Unmarshallable<JarEntry> u = dtoUnmarshaller.newUnmarshaller(JarEntry.class); javaService.getEntry(projectConfig.getPath(), libId, path, new AsyncRequestCallback<JarEntry>(u) { @Override protected void onSuccess(JarEntry entry) { Node node = createNode(entry, libId, projectConfig, settingsProvider.getSettings()); callback.onSuccess(node); } @Override protected void onFailure(Throwable exception) { callback.onFailure(exception); } }); } }); } @Override public Function<List<ItemReference>, Promise<List<ItemReference>>> filterItemReference() { return new Function<List<ItemReference>, Promise<List<ItemReference>>>() { @Override public Promise<List<ItemReference>> apply(List<ItemReference> referenceList) throws FunctionException { final List<ItemReference> collector = new ArrayList<>(); Promise<Void> promise = Promises.resolve(null); return getNonEmptyChildren(promise, referenceList.listIterator(), collector) .thenPromise(new Function<Void, Promise<List<ItemReference>>>() { @Override public Promise<List<ItemReference>> apply(Void arg) throws FunctionException { return Promises.resolve(collector); } }); } }; } private Promise<Void> getNonEmptyChildren(Promise<Void> promise, ListIterator<ItemReference> iterator, final List<ItemReference> collector) { if (!iterator.hasNext()) { return promise; } final ItemReference itemReference = iterator.next(); if (itemReference.getType().equals("file")) { collector.add(itemReference); return getNonEmptyChildren(promise, iterator, collector); } final Promise<Void> derivedPromise = promise.thenPromise(new Function<Void, Promise<Void>>() { @Override public Promise<Void> apply(Void arg) throws FunctionException { return foundFirstNonEmpty(itemReference).thenPromise(new Function<List<ItemReference>, Promise<Void>>() { @Override public Promise<Void> apply(List<ItemReference> arg) throws FunctionException { collector.addAll(arg); return Promises.resolve(null); } }); } }); return getNonEmptyChildren(derivedPromise, iterator, collector); } @Override protected Function<List<Node>, Promise<List<Node>>> sortNodes() { return new Function<List<Node>, Promise<List<Node>>>() { @Override public Promise<List<Node>> apply(List<Node> nodes) throws FunctionException { Collections.sort(nodes, new FQNComparator()); return Promises.resolve(nodes); } }; } private Promise<List<ItemReference>> foundFirstNonEmpty(ItemReference parent) { return AsyncPromiseHelper.createFromAsyncRequest(getItemReferenceRC(parent.getPath())) .thenPromise(checkForEmptiness(parent)); } private Function<List<ItemReference>, Promise<List<ItemReference>>> checkForEmptiness(final ItemReference parent) { return new Function<List<ItemReference>, Promise<List<ItemReference>>>() { @Override public Promise<List<ItemReference>> apply(List<ItemReference> children) throws FunctionException { if (children.isEmpty() || children.size() > 1) { List<ItemReference> list = new ArrayList<>(); list.add(parent); return Promises.resolve(list); } if ("file".equals(children.get(0).getType())) { List<ItemReference> list = new ArrayList<>(); list.add(parent); return Promises.resolve(list); } else { return foundFirstNonEmpty(children.get(0)); } } }; } }