/******************************************************************************* * Copyright (c) 2016 ARM Ltd. and others * 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: * ARM Ltd and ARM Germany GmbH - Initial API and implementation *******************************************************************************/ package com.arm.cmsis.pack.installer.ui.views; import java.util.Collection; import java.util.LinkedList; import java.util.regex.Pattern; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Tree; import com.arm.cmsis.pack.CpPlugIn; import com.arm.cmsis.pack.ICpEnvironmentProvider; import com.arm.cmsis.pack.ICpPackInstaller; import com.arm.cmsis.pack.common.CmsisConstants; import com.arm.cmsis.pack.data.CpComponent; import com.arm.cmsis.pack.data.CpItem; import com.arm.cmsis.pack.data.ICpBoard; import com.arm.cmsis.pack.data.ICpComponent; import com.arm.cmsis.pack.data.ICpDeviceItem; import com.arm.cmsis.pack.data.ICpExample; import com.arm.cmsis.pack.data.ICpFile; import com.arm.cmsis.pack.data.ICpItem; import com.arm.cmsis.pack.data.ICpPack; import com.arm.cmsis.pack.data.ICpPack.PackState; import com.arm.cmsis.pack.events.RteEvent; import com.arm.cmsis.pack.generic.ITreeObject; import com.arm.cmsis.pack.installer.ui.CpInstallerPlugInUI; import com.arm.cmsis.pack.installer.ui.IHelpContextIds; import com.arm.cmsis.pack.installer.ui.Messages; import com.arm.cmsis.pack.installer.ui.PackInstallerViewController; import com.arm.cmsis.pack.ui.CpPlugInUI; import com.arm.cmsis.pack.ui.tree.TreeObjectContentProvider; import com.arm.cmsis.pack.utils.AlnumComparator; import com.arm.cmsis.pack.utils.VersionComparator; /** * Default implementation of the pack properties view in pack manager */ public class PackPropertyView extends PackInstallerView { public static final String ID = "com.arm.cmsis.pack.installer.ui.views.PackPropertyView"; //$NON-NLS-1$ private Action fInstallPackAction; // install single pack private Action fInstallRequiredPacksAction; // install required packs private Action fCopyAction; /** * */ class PackPropertyViewContentProvider extends TreeObjectContentProvider { private ICpItem root; @Override public Object[] getChildren(Object parentElement) { ICpItem item = getCpItem(parentElement); if (item == null) { return ITreeObject.EMPTY_OBJECT_ARRAY; } // root node if (item instanceof ICpPack) { return getPackChildren((ICpPack) item); } // device node else if (item instanceof ICpDeviceItem) { return getDeviceItems((ICpDeviceItem) item); } // board node else if (item instanceof ICpBoard) { return getBoardChildren((ICpBoard) item); } // example node else if (item instanceof ICpExample) { return getExampleChildren((ICpExample) item); } // examples node else if (CmsisConstants.EXAMPLES_TAG.equals(item.getTag())) { return getExamples(item); } // components node else if (CmsisConstants.COMPONENTS_TAG.equals(item.getTag())) { root = buildComponentTree(item); return root.getChildArray(); } // required packages node else if (CmsisConstants.PACKAGES_TAG.equals(item.getTag())) { return getRequiredPacks(item); } return super.getChildren(parentElement); } @Override public boolean hasChildren(Object element) { return getChildren(element) != null && getChildren(element).length > 0; } private Object[] getPackChildren(ICpPack pack) { if (pack == null || pack.getPackState() == PackState.ERROR || pack.getChildren() == null) { return ITreeObject.EMPTY_OBJECT_ARRAY; } Collection<ICpItem> result = new LinkedList<>(); for (ICpItem child : pack.getChildren()) { if (CmsisConstants.DEVICES_TAG.equals(child.getTag()) || CmsisConstants.BOARDS_TAG.equals(child.getTag()) || CmsisConstants.COMPONENTS_TAG.equals(child.getTag())) { result.add(child); } // requirements node, here we only need the required packages else if (CmsisConstants.REQUIREMENTS_TAG.equals(child.getTag())) { result.add(child.getFirstChild(CmsisConstants.PACKAGES_TAG)); } // examples node else if (CmsisConstants.EXAMPLES_TAG.equals(child.getTag())) { Object[] examples = getExamples(child); if (examples != null && examples.length > 0) { result.add(child); } } } return result.toArray(); } private Object[] getRequiredPacks(ICpItem packs) { if (packs == null || packs.getChildren() == null) { return ITreeObject.EMPTY_OBJECT_ARRAY; } return packs.getChildArray(); } private Object[] getDeviceItems(ICpDeviceItem device) { if (device.getDeviceItems() != null) { return device.getDeviceItems().toArray(); } return ITreeObject.EMPTY_OBJECT_ARRAY; } private Object[] getBoardChildren(ICpBoard board) { // Currently we show nothing under the board item return ITreeObject.EMPTY_OBJECT_ARRAY; } private Object[] getExamples(ICpItem examples) { if (examples == null || examples.getChildren() == null) { return ITreeObject.EMPTY_OBJECT_ARRAY; } Collection<ICpExample> result = new LinkedList<>(); ICpEnvironmentProvider envProvider = CpPlugIn.getEnvironmentProvider(); for (ICpItem item : examples.getChildren()) { if (!(item instanceof ICpExample)) { continue; } ICpExample example = (ICpExample) item; if(envProvider == null || !envProvider.isSupported(example)) { continue; } result.add(example); } return result.toArray(); } private Object[] getExampleChildren(ICpExample example) { if (example == null || example.getChildren() == null) { return ITreeObject.EMPTY_OBJECT_ARRAY; } Collection<ICpItem> result = new LinkedList<>(); for (ICpItem item : example.getChildren()) { // Only show the board item under the example item if (item instanceof ICpBoard) { result.add(item); break; } } return result.toArray(); } /** * Build the Compoenent Tree of this pack * @param parent the default root of this tree * @return the root of this component tree */ protected ICpItem buildComponentTree(ICpItem parent) { ICpItem newRoot = new CpItem(null, parent.getTag()); if (parent.getChildren() == null) { return newRoot; } for (ICpItem child : parent.getChildren()) { if (CmsisConstants.BUNDLE_TAG.equals(child.getTag())) { for (ICpItem gchild : child.getChildren()) { if (!CmsisConstants.COMPONENT_TAG.equals(gchild.getTag())) { continue; } String id = gchild.getId(); addChild(id, newRoot); } } else if (CmsisConstants.COMPONENT_TAG.equals(child.getTag())) { String id = child.getId(); addChild(id, newRoot); } } return newRoot; } /** * Add leaf to the parent node * @param id ID of the component to be added as a child of parent, used to check if the node with this ID already exists * @param parent parent node to which child node should be added */ protected void addChild(String id, ICpItem parent) { String path = formalizeId(id); ICpItem node = findNode(path, parent); if (node == null) { int lastDot = path.lastIndexOf('.'); String parentPath = path.substring(0, lastDot); node = buildPath(parentPath, parent); node.addChild(new CpComponent(node, path.substring(lastDot + 1))); } } /** * Turn the ID to the format: a.b.c to find the path to the child * @param id ID of the component * @return formalized ID */ private String formalizeId(String id) { int begin = id.lastIndexOf(CmsisConstants.DOUBLE_COLON) + 2; int end = id.lastIndexOf('('); if (end == -1) { end = id.lastIndexOf(':'); } return id.substring(begin, end); } /** * Find if the node already exists under the parent node * @param path formed like a.b.c, each segment represents a child node * @param parent the parent node * @return the child node if it has the formed ID like a.b.c, null if it does not exist */ private ICpItem findNode(String path, ICpItem parent) { String[] segments = path.split(Pattern.quote(".")); //$NON-NLS-1$ ICpItem p = parent; for (String segment : segments) { if (p.getFirstChild(segment) != null) { p = p.getFirstChild(segment); } else { return null; } } return p; } /** * Build the child nodes along the path like a.b.c, each segment represents a child node * @param path formed like a.b.c * @param parent the parent node * @return the leaf node on the path under parent node */ private ICpItem buildPath(String path, ICpItem parent) { String[] segments = path.split(Pattern.quote(".")); //$NON-NLS-1$ ICpItem p = parent; for (String segment : segments) { if (p.getFirstChild(segment) == null || !p.getFirstChild(segment).hasChildren()) { p.addChild(new CpComponent(p, segment)); } p = p.getFirstChild(segment); } return p; } } class PackPropertyViewLabelProvider extends ColumnLabelProvider { @Override public Image getImage(Object obj) { ICpItem item = getCpItem(obj); if (item == null) { return null; } ICpPack pack = item.getPack(); boolean installed = pack != null ? pack.getPackState() == PackState.INSTALLED : false; // root node if (pack != null && item == pack) { if (installed) { if (pack.isRequiredPacksInstalled()) { return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGE); } return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGE_RED); } return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGE_GREY); } // Component node else if (item instanceof ICpComponent) { ICpComponent c = (ICpComponent) item; if(c.getMaxInstances() > 1) { return CpPlugInUI.getImage(CpPlugInUI.ICON_COMPONENT); } return CpPlugInUI.getImage(CpPlugInUI.ICON_COMPONENT); } // File node else if (item instanceof ICpFile) { return CpPlugInUI.getImage(CpPlugInUI.ICON_FILE); } // Board node else if (item instanceof ICpBoard) { if (item.isDeprecated()) { return CpPlugInUI.getImage(CpPlugInUI.ICON_BOARD_DEPR); } if (installed) { return CpPlugInUI.getImage(CpPlugInUI.ICON_BOARD); } return CpPlugInUI.getImage(CpPlugInUI.ICON_BOARD_GREY); } // Device node else if (item instanceof ICpDeviceItem) { ICpDeviceItem di = (ICpDeviceItem) item; if (di.getDeviceItems() == null) { if (di.isDeprecated()) { return CpPlugInUI.getImage(CpPlugInUI.ICON_DEVICE_DEPR); } if (installed) { return CpPlugInUI.getImage(CpPlugInUI.ICON_DEVICE); } return CpPlugInUI.getImage(CpPlugInUI.ICON_DEVICE_GREY); } return CpPlugInUI.getImage(CpPlugInUI.ICON_COMPONENT_CLASS); } switch (item.getTag()) { case CmsisConstants.COMPONENTS_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_RTE); case CmsisConstants.DEVICES_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_DEVICE); case CmsisConstants.BOARDS_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_BOARD); case CmsisConstants.BUNDLE_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_RTE); case CmsisConstants.COMPATIBLE_DEVICE_TAG: case CmsisConstants.MOUNTED_DEVICE_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_DEVICE); case CmsisConstants.EXAMPLES_TAG: return CpPlugInUI.getImage(CpPlugInUI.ICON_EXAMPLE); case CmsisConstants.PACKAGES_TAG: if (pack != null && pack.getPackState() == PackState.INSTALLED && !pack.isRequiredPacksInstalled()) { return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGES_RED); } return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGES); case CmsisConstants.PACKAGE_TAG: String familyId = item.getAttribute(CmsisConstants.VENDOR) + '.' + item.getAttribute(CmsisConstants.NAME); if (isRequiredPackInstalled(familyId, item.getAttribute(CmsisConstants.VERSION))) { return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGE); } return CpPlugInUI.getImage(CpPlugInUI.ICON_PACKAGE_RED); default: break; } return null; } @Override public String getText(Object element) { ICpItem item = getCpItem(element); if (item == null) { return CmsisConstants.EMPTY_STRING; } if (item instanceof ICpComponent) { return capitalizeInitChar(item.getTag()); } // required package node else if (!(item instanceof ICpPack) && CmsisConstants.PACKAGE_TAG.equals(item.getTag())) { return item.getAttribute(CmsisConstants.VENDOR) + '.' + item.getAttribute(CmsisConstants.NAME) + '.' + '[' + item.getAttribute(CmsisConstants.VERSION) + ']'; } return capitalizeInitChar(item.getId()); } private String capitalizeInitChar(String string) { if (string.isEmpty()) { return string; } char c = Character.toUpperCase(string.charAt(0)); return String.valueOf(c) + string.substring(1); } } class NameSorter extends ViewerSorter { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (!(e1 instanceof ICpItem) || !(e2 instanceof ICpItem)) { return 0; } ICpItem i1 = (ICpItem) e1; ICpItem i2 = (ICpItem) e2; return new AlnumComparator(false).compare(i1.getId(), i2.getId()); } } public PackPropertyView() { fViewController = CpInstallerPlugInUI.getViewController(); } @Override public boolean isFilterClient() { return false; } @Override protected String getHelpContextId() { return IHelpContextIds.PACK_PROPERTIES_VIEW; } @Override public void createTreeColumns() { fViewer.setContentProvider(new PackPropertyViewContentProvider()); fViewer.setLabelProvider(new PackPropertyViewLabelProvider()); fViewer.setSorter(new NameSorter()); Tree tree = fViewer.getTree(); tree.setHeaderVisible(false); tree.setLinesVisible(false); } @Override protected void refresh() { ICpPack pack = fViewController.getSelectedPack(); if (pack != null) { ICpItem proot = new CpItem(null); proot.addChild(pack); fViewer.setAutoExpandLevel(2); fViewer.setInput(proot); } else { fViewer.setInput(null); } } ICpExample getExampleItem() { IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection(); if (selection == null || selection.isEmpty()) { return null; } Object obj = selection.getFirstElement(); if (obj instanceof ICpExample) { return (ICpExample) obj; } return null; } /** * Check if the required pack is installed * @param familyId family ID of the required pack * @param versionRange version range of the required pack * @return true if the required pack with the version range is installed */ boolean isRequiredPackInstalled(String familyId, String versionRange) { Collection<ICpPack> installedPacks = CpPlugIn.getPackManager().getInstalledPacks().getPacksByPackFamilyId(familyId); if (installedPacks == null) { return false; } for (ICpPack installedPack : installedPacks) { if (VersionComparator.matchVersionRange(installedPack.getVersion(), versionRange)) { return true; } } return false; } @Override protected void makeActions() { super.makeActions(); fInstallPackAction = new Action() { @Override public void run() { ICpPack pack = fViewController.getSelectedPack(); if (pack == null) { return; } ICpPackInstaller packInstaller = CpPlugIn.getPackManager().getPackInstaller(); if(packInstaller != null) { packInstaller.installPack(pack.getPackId()); } } }; fInstallRequiredPacksAction = new Action() { @Override public void run() { ICpPack pack = fViewController.getSelectedPack(); if (pack == null) { return; } ICpPackInstaller packInstaller = CpPlugIn.getPackManager().getPackInstaller(); if(packInstaller != null) { packInstaller.installRequiredPacks(pack); } } }; fCopyAction = new Action() { @Override public void run() { ICpExample cpExample = getExampleItem(); if(cpExample != null) { CpInstallerPlugInUI.getViewController().copyExample(cpExample); } } }; fCopyAction.setText(Messages.PackPropertyView_CopyAction); fCopyAction.setToolTipText(Messages.PackPropertyView_CopyTooltip); fCopyAction.setImageDescriptor(CpPlugInUI.getImageDescriptor(CpPlugInUI.ICON_RTE)); } @Override protected void fillContextMenu(IMenuManager manager) { super.fillContextMenu(manager); manager.add(new Separator()); ICpPackInstaller packInstaller = CpPlugIn.getPackManager().getPackInstaller(); if(packInstaller == null) { return; } ICpExample example = getExampleItem(); ICpPack pack = null; PackState packState = PackState.UNKNOWN; if (example != null) { pack = example.getPack(); packState = pack.getPackState(); if (packState == PackState.INSTALLED) { manager.add(fCopyAction); fCopyAction.setEnabled(true); return; } } else { pack = fViewController.getSelectedPack(); } if (pack == null) { return; } packState = pack.getPackState(); if (packState == PackState.DOWNLOADED) { fInstallPackAction.setText(Messages.PackPropertyView_UnpackAction); fInstallPackAction.setToolTipText(Messages.PackPropertyView_UnpackTooltip); fInstallPackAction.setImageDescriptor(CpPlugInUI.getImageDescriptor(CpPlugInUI.ICON_RTE_UNPACK)); manager.add(fInstallPackAction); } else if (packState == PackState.AVAILABLE) { fInstallPackAction.setText(Messages.PackPropertyView_InstallAction); fInstallPackAction.setToolTipText(Messages.PackPropertyView_InstallTooltip); fInstallPackAction.setImageDescriptor(CpPlugInUI.getImageDescriptor(CpPlugInUI.ICON_RTE_INSTALL)); manager.add(fInstallPackAction); } else if (!pack.isRequiredPacksInstalled()) { fInstallRequiredPacksAction.setText(Messages.PackInstallerView_InstallRequiredPacks); fInstallRequiredPacksAction.setToolTipText(Messages.PackInstallerView_InstallRequiredPacksToolTip); fInstallRequiredPacksAction.setImageDescriptor(CpPlugInUI.getImageDescriptor(CpPlugInUI.ICON_RTE_INSTALL)); manager.add(fInstallRequiredPacksAction); } } @Override public void handleRteEvent(RteEvent event) { String topic = event.getTopic(); switch(topic) { case RteEvent.PACKS_RELOADED: case PackInstallerViewController.INSTALLER_UI_PACK_CHANGED: refresh(); default: super.handleRteEvent(event); } } }