/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.communication.views.contributors; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.LogFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import de.rcenvironment.core.component.model.api.ComponentInstallation; import de.rcenvironment.core.component.model.api.ComponentInterface; import de.rcenvironment.core.gui.communication.views.model.NetworkGraphNodeWithContext; import de.rcenvironment.core.gui.communication.views.model.NetworkGraphNodeWithContext.Context; import de.rcenvironment.core.gui.communication.views.spi.ContributedNetworkViewNode; import de.rcenvironment.core.gui.communication.views.spi.NetworkViewContributor; import de.rcenvironment.core.gui.resources.api.ImageManager; import de.rcenvironment.core.gui.resources.api.StandardImages; import de.rcenvironment.core.utils.common.StringUtils; /** * Contributes instance subtrees showing the local and published components of a node. * * @author Robert Mischke * @author Sascha Zur (original component image handling) * @author Doreen Seider (original component image handling) */ public class InstanceComponentsInfoContributor extends NetworkViewContributorBase { private static final int INSTANCE_ELEMENTS_PRIORITY = 10; /** * A tree node containing node representing published or local component. * * @author Robert Mischke */ private class ComponentFolderNode implements ContributedNetworkViewNode { private final NetworkGraphNodeWithContext instanceNode; private final boolean typeIsPublic; ComponentFolderNode(NetworkGraphNodeWithContext instanceNode, boolean typeIsPublic) { this.instanceNode = instanceNode; this.typeIsPublic = typeIsPublic; } @Override public NetworkViewContributor getContributor() { return InstanceComponentsInfoContributor.this; } public NetworkGraphNodeWithContext getInstanceNode() { return instanceNode; } public boolean getTypeIsPublic() { return typeIsPublic; } } private Image folderImage; private final Map<String, Image> componentIconCache; private Image componentFallbackImage; public InstanceComponentsInfoContributor() { ImageManager imageManager = ImageManager.getInstance(); folderImage = imageManager.getSharedImage(StandardImages.FOLDER_16); componentFallbackImage = imageManager.getSharedImage(StandardImages.RCE_LOGO_16); componentIconCache = new HashMap<String, Image>(); } @Override public int getRootElementsPriority() { return 0; // disabled } @Override public Object[] getTopLevelElements(Object parentNode) { return null; // disabled } @Override public int getInstanceDataElementsPriority() { return INSTANCE_ELEMENTS_PRIORITY; } @Override public Object[] getChildrenForNetworkInstanceNode(NetworkGraphNodeWithContext parentNode) { List<Object> result = new ArrayList<>(2); // max. expected number if (currentModel.componentKnowledge != null) { // only show the "published" folder when there are published components Collection<ComponentInstallation> publishedInstallations = currentModel.componentKnowledge.getPublishedInstallationsOnNode(parentNode.getNode().getNodeId()); if (publishedInstallations != null && publishedInstallations.size() != 0) { result.add(new ComponentFolderNode(parentNode, true)); } if (parentNode.isLocalNode()) { result.add(new ComponentFolderNode(parentNode, false)); } } return result.toArray(); } @Override public boolean hasChildren(Object parentNode) { if (parentNode instanceof ComponentFolderNode) { ComponentFolderNode typedNode = (ComponentFolderNode) parentNode; if (typedNode.getTypeIsPublic()) { return true; // otherwise, the folder wouldn't exist } else { Collection<ComponentInstallation> localInstallations = determineLocalComponents(typedNode.getInstanceNode()); return !localInstallations.isEmpty(); } } if (parentNode instanceof NetworkGraphNodeWithContext) { assertIsComponentNode((NetworkGraphNodeWithContext) parentNode); // consistency check return false; } throw newUnexpectedCallException(); } @Override public Object[] getChildren(Object parentNode) { // only expecting ComponentFolderNode instances as parent final ComponentFolderNode typedParentNode = (ComponentFolderNode) parentNode; final NetworkGraphNodeWithContext instanceNode = typedParentNode.getInstanceNode(); if (typedParentNode.getTypeIsPublic()) { Collection<ComponentInstallation> publishedInstallations = currentModel.componentKnowledge.getPublishedInstallationsOnNode(instanceNode.getNode().getNodeId()); // note: the parent node is only created if the list is defined and not empty return createNodesForComponentInstallations(instanceNode, publishedInstallations); } else { Collection<ComponentInstallation> localInstallations = determineLocalComponents(instanceNode); return createNodesForComponentInstallations(instanceNode, localInstallations); } } @Override public Object getParent(Object node) { if (node instanceof ComponentFolderNode) { return ((ComponentFolderNode) node).getInstanceNode(); } else if (node instanceof NetworkGraphNodeWithContext) { return ((NetworkGraphNodeWithContext) node).getParent(); } else { return null; // error - will trigger an upstream warning } } @Override public String getText(Object node) { if (node instanceof ComponentFolderNode) { ComponentFolderNode typedNode = (ComponentFolderNode) node; if (typedNode.getTypeIsPublic()) { return "Published Components"; } else { return "Local Components"; } } if (node instanceof NetworkGraphNodeWithContext) { NetworkGraphNodeWithContext typedNode = (NetworkGraphNodeWithContext) node; assertIsComponentNode(typedNode); // consistency check ComponentInterface componentInterface = typedNode.getComponentInstallation().getComponentRevision().getComponentInterface(); // Should be improved because using plain ids here is weird. // Plain ids are used to not introduce a new dependency to core.component.integration as this is not good from a (kind of) // communication bundle. But as the network view is not showing only communication stuff anymore, this dependency thing is // probably obsolete -- seid_do, Aug 2014 if (componentInterface.getVersion() != null && componentInterface.getIdentifier().startsWith("de.rcenvironment.integration.common.") || componentInterface.getIdentifier().startsWith("de.rcenvironment.integration.cpacs.")) { return StringUtils.format("%s (%s)", componentInterface.getDisplayName(), componentInterface.getVersion()); } else { return StringUtils.format("%s", componentInterface.getDisplayName()); } } throw newUnexpectedCallException(); } @Override public Image getImage(Object node) { if (node instanceof ComponentFolderNode) { return folderImage; } if (node instanceof NetworkGraphNodeWithContext) { NetworkGraphNodeWithContext typedNode = (NetworkGraphNodeWithContext) node; assertIsComponentNode(typedNode); // consistency check ComponentInstallation installation = typedNode.getComponentInstallation(); if (installation != null) { // FIXME improve caching key; temporary String cacheKey = installation.getInstallationId(); Image image = componentIconCache.get(cacheKey); if (image == null) { try { byte[] iconData = installation.getComponentRevision().getComponentInterface().getIcon16(); // TODO review: dispose Image instance? or cache Image instance instead? ImageDescriptor iDescr = ImageDescriptor.createFromImage(new Image(Display.getCurrent(), new ByteArrayInputStream(iconData))); image = iDescr.createImage(); } catch (RuntimeException e) { image = componentFallbackImage; // set fallback in case of errors } componentIconCache.put(cacheKey, image); } return image; } else { // fallback; should never happen LogFactory.getLog(getClass()).warn( "Found a network view component node without a component installation: " + typedNode.getDisplayNameOfNode()); return componentFallbackImage; } } throw newUnexpectedCallException(); } @Override public void dispose() { // dispose cached component resources/icons for (Image image : componentIconCache.values()) { if (image != componentFallbackImage) { image.dispose(); } } // note: not disposing folderImage and componentFallbackImage, as they are shared } private Object[] createNodesForComponentInstallations(NetworkGraphNodeWithContext node, Collection<ComponentInstallation> installations) { Object[] result = new Object[installations.size()]; int i = 0; for (ComponentInstallation installation : installations) { if (installation == null) { LogFactory.getLog(getClass()).warn("Skipping 'null' component installation for node " + node.getNode().getNodeId()); continue; } NetworkGraphNodeWithContext newChild = new NetworkGraphNodeWithContext(node, Context.COMPONENT_INSTALLATION, this); newChild.setComponentInstallation(installation); result[i++] = newChild; } Arrays.sort(result); return result; } private Collection<ComponentInstallation> determineLocalComponents(NetworkGraphNodeWithContext typedParentNode) { Collection<ComponentInstallation> localInstallations = currentModel.componentKnowledge.getLocalInstallations(); // hide all published local components from "local components" list Collection<ComponentInstallation> publishedLocalInstallations = currentModel.componentKnowledge.getPublishedInstallationsOnNode(typedParentNode.getNode().getNodeId()); if (publishedLocalInstallations != null) { localInstallations = new ArrayList<ComponentInstallation>(localInstallations); localInstallations.removeAll(publishedLocalInstallations); } return localInstallations; } private void assertIsComponentNode(NetworkGraphNodeWithContext typedNode) { if (typedNode.getContext() != Context.COMPONENT_INSTALLATION) { throw new IllegalStateException("Unexpected context: " + typedNode.getContext()); } } }