/*******************************************************************************
* Copyright (c) 2015 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.rte;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import com.arm.cmsis.pack.CpPlugIn;
import com.arm.cmsis.pack.ICpEnvironmentProvider;
import com.arm.cmsis.pack.ICpPackManager;
import com.arm.cmsis.pack.common.CmsisConstants;
import com.arm.cmsis.pack.data.CpConditionContext;
import com.arm.cmsis.pack.data.CpItem;
import com.arm.cmsis.pack.data.CpPackFilter;
import com.arm.cmsis.pack.data.ICpComponent;
import com.arm.cmsis.pack.data.ICpConditionContext;
import com.arm.cmsis.pack.data.ICpDeviceItem;
import com.arm.cmsis.pack.data.ICpFile;
import com.arm.cmsis.pack.data.ICpGenerator;
import com.arm.cmsis.pack.data.ICpItem;
import com.arm.cmsis.pack.data.ICpPack;
import com.arm.cmsis.pack.data.ICpPackCollection;
import com.arm.cmsis.pack.data.ICpPackFilter;
import com.arm.cmsis.pack.data.ICpTaxonomy;
import com.arm.cmsis.pack.enums.EEvaluationResult;
import com.arm.cmsis.pack.enums.EVersionMatchMode;
import com.arm.cmsis.pack.info.CpComponentInfo;
import com.arm.cmsis.pack.info.CpFileInfo;
import com.arm.cmsis.pack.info.ICpComponentInfo;
import com.arm.cmsis.pack.info.ICpConfigurationInfo;
import com.arm.cmsis.pack.info.ICpDeviceInfo;
import com.arm.cmsis.pack.info.ICpFileInfo;
import com.arm.cmsis.pack.info.ICpPackFilterInfo;
import com.arm.cmsis.pack.info.ICpPackInfo;
import com.arm.cmsis.pack.rte.components.IRteComponent;
import com.arm.cmsis.pack.rte.components.IRteComponentGroup;
import com.arm.cmsis.pack.rte.components.IRteComponentItem;
import com.arm.cmsis.pack.rte.components.RteComponentRoot;
import com.arm.cmsis.pack.rte.components.RteMoreClass;
import com.arm.cmsis.pack.rte.components.RteSelectedDeviceClass;
import com.arm.cmsis.pack.rte.dependencies.IRteDependencyItem;
import com.arm.cmsis.pack.rte.dependencies.IRteDependencySolver;
import com.arm.cmsis.pack.rte.dependencies.RteDependencySolver;
import com.arm.cmsis.pack.rte.devices.IRteDeviceItem;
import com.arm.cmsis.pack.rte.devices.RteDeviceItem;
import com.arm.cmsis.pack.utils.Utils;
/**
* Default implementation of IRteModel interface
*
*/
public class RteModel implements IRteModel {
// object to store/load configuration meta data
protected ICpConfigurationInfo fConfigurationInfo = null;
// filtered Packs
protected ICpPackCollection fAllPacks = null;
protected Collection<ICpPack> fFilteredPacks = null;
protected ICpPackFilter fPackFilter = null;
protected Map<String, ICpPackInfo> fUsedPackInfos = null;
// selected device
protected ICpDeviceInfo fDeviceInfo = null;
// selected toolchain
protected ICpItem fToolchainInfo = null;
// component filter
protected ICpConditionContext fComponentFilter = null;
// filtered components tree
protected RteComponentRoot fComponentRoot = null;
// filtered device tree
protected IRteDeviceItem fRteDevices = null;
// engine to evaluate/resolve component dependencies
protected IRteDependencySolver fDependencySolver = null;
protected Map<String, ICpPack> fGeneratedPacks = null; // read from configuration
/**
* Default constructor
*/
public RteModel() {
fDependencySolver = new RteDependencySolver(this);
}
@Override
public void clear() {
fAllPacks = null;
fRteDevices = null;
fComponentRoot = null;
fPackFilter = null;
fFilteredPacks = null;
fGeneratedPacks = null;
fDeviceInfo = null;
fToolchainInfo = null;
fConfigurationInfo = null;
}
@Override
public ICpConfigurationInfo getConfigurationInfo() {
return fConfigurationInfo;
}
@Override
public Map<String, ICpPack> getGeneratedPacks() {
return fGeneratedPacks;
}
@Override
public ICpPack getGeneratedPack(String gpdsc) {
if(fGeneratedPacks != null)
return fGeneratedPacks.get(gpdsc);
return null;
}
@Override
public boolean isGeneratedPackUsed(String gpdsc) {
if(fGeneratedPacks != null)
return fGeneratedPacks.containsKey(gpdsc);
return false;
}
@Override
public void setConfigurationInfo(ICpConfigurationInfo info) {
fConfigurationInfo = info;
if(fConfigurationInfo == null) {
clear();
return;
}
fDeviceInfo = info.getDeviceInfo();
fToolchainInfo = info.getToolChainInfo();
fPackFilter = new CpPackFilter(info.createPackFilter());
update();
}
@Override
public void update(){
update(RteConstants.NONE);
}
@Override
public void update(int flags){
fRteDevices = null;
collectPacks();
filterPacks();
resolveFilterPacks();
getDevices(); // creates device tree
resolveDevice();
updateComponentFilter();
collectComponents();
resolveComponents(flags);
updateComponentInfos();
}
protected void collectPacks() {
fAllPacks = null;
fGeneratedPacks = null;
ICpPackManager pm = CpPlugIn.getPackManager();
if(pm == null)
return;
fAllPacks = pm.getInstalledPacks();
// collect and load generated packs
collectGeneratedPacks();
}
protected void collectGeneratedPacks() {
fGeneratedPacks = new HashMap<String, ICpPack>();
Collection<? extends ICpItem> children = fConfigurationInfo.getGrandChildren(CmsisConstants.COMPONENTS_TAG);
if(children == null)
return;
ICpPackManager pm = CpPlugIn.getPackManager();
for(ICpItem item : children) {
if(!item.hasAttribute(CmsisConstants.GENERATOR))
continue;
if(!(item instanceof ICpComponentInfo))
continue;
ICpComponentInfo ci = (ICpComponentInfo)item;
if(ci.isGenerated())
continue; // consider only bootstrap
String gpdsc = ci.getGpdsc(true);
if(gpdsc == null || gpdsc.isEmpty())
continue;
if(fGeneratedPacks.containsKey(gpdsc)) {
ICpPack pack = fGeneratedPacks.get(gpdsc);
if(pack != null || !ci.isSaved())
continue;
}
ICpPack pack = pm.loadGpdsc(gpdsc);
fGeneratedPacks.put(gpdsc, pack);
}
}
protected void filterPacks() {
fFilteredPacks = null;
if(fAllPacks != null) {
fPackFilter.setLatestPackIDs(fAllPacks.getLatestPackIDs());
fFilteredPacks = fAllPacks.getFilteredPacks(fPackFilter);
}
}
protected boolean resolveFilterPacks() {
if(fConfigurationInfo == null) {
return false;
}
boolean allResolved = true;
ICpPackFilterInfo packsItem = fConfigurationInfo.getPackFilterInfo();
if(packsItem == null) {
return allResolved;
}
Collection<? extends ICpItem> packInfos = packsItem.getChildren();
if(packInfos == null) {
return allResolved;
}
if(fAllPacks == null) {
return false;
}
for(ICpItem item : packInfos) {
if(!(item instanceof ICpPackInfo)) {
continue;
}
ICpPackInfo packInfo = (ICpPackInfo)item;
EVersionMatchMode mode = packInfo.getVersionMatchMode();
ICpPack pack = null;
switch(mode){
case FIXED:
pack = fAllPacks.getPack(packInfo.getId());
break;
case EXCLUDED:
case LATEST:
pack = fAllPacks.getPack(packInfo.getPackFamilyId());
break;
}
packInfo.setPack(pack);
if(pack == null && mode !=EVersionMatchMode.EXCLUDED) {
allResolved = false;
}
}
return allResolved;
}
protected ICpPack resolvePack(ICpPackInfo pi) {
ICpPack pack = pi.getPack();
if(pack != null) {
return pack;
}
if(fAllPacks == null) {
return null;
}
pack = fAllPacks.getPack(pi.getId());
if(pack != null) {
pi.setPack(pack);
}
return pack;
}
protected boolean resolveDevice() {
if(fDeviceInfo == null) {
return false;
}
fDeviceInfo.setRteDevice(null);
IRteDeviceItem rteDevice = getDevices().findItem(fDeviceInfo.attributes());
fDeviceInfo.setRteDevice(rteDevice);
ICpPackInfo packInfo = fDeviceInfo.getPackInfo();
if(rteDevice == null) {
resolvePack(packInfo);
}
ICpDeviceItem device = fDeviceInfo.getDevice();
EEvaluationResult res = EEvaluationResult.FULFILLED;
if(device == null) {
if(packInfo.getPack() == null) {
res = EEvaluationResult.FAILED;
} else {
res = EEvaluationResult.UNAVAILABLE_PACK;
}
}
fDeviceInfo.setEvaluationResult(res);
return device != null;
}
@Override
public void updateComponentInfos() {
if(fConfigurationInfo == null) {
return;
}
ICpItem apiInfos = fConfigurationInfo.getApisItem();
apiInfos.clear();
ICpItem componentInfos = fConfigurationInfo.getComponentsItem();
componentInfos.clear();
fUsedPackInfos = new HashMap<String, ICpPackInfo>();
ICpPackInfo devicePackInfo = fDeviceInfo.getPackInfo();
addUsedPackInfo(devicePackInfo.getPackInfo());
Map<ICpComponent, EVersionMatchMode> selectedApis = new HashMap<ICpComponent, EVersionMatchMode>();
Collection<IRteComponent> selectedComponents = getSelectedComponents();
for(IRteComponent component : selectedComponents){
ICpComponent c = component.getActiveCpComponent();
if(c == null) {
continue;
}
ICpComponentInfo ci = null;
if(c instanceof ICpComponentInfo) {
// unresolved component, leave as is
ci = (ICpComponentInfo)c;
ci.setParent(componentInfos);
} else {
ICpGenerator gen = c.getGenerator();
if( gen != null) {
// keep generator component info as is
ci = component.getActiveCpComponentInfo();
}
if(ci == null) {
ci = new CpComponentInfo(componentInfos, c, component.getSelectedCount());
if(gen != null) {
ICpItem gpdscItem = new CpItem(ci, CmsisConstants.GPDSC_TAG);
ci.addChild(gpdscItem);
gpdscItem.attributes().setAttribute(CmsisConstants.NAME, gen.getGpdsc());
}
} else {
ci.setComponent(c);
ci.setParent(componentInfos);
}
collectFilteredFiles(ci ,c);
}
EVersionMatchMode versionMode = component.isUseLatestVersion() ?
EVersionMatchMode.LATEST: EVersionMatchMode.FIXED;
ci.setVersionMatchMode(versionMode);
componentInfos.addChild(ci);
component.setActiveComponentInfo(ci);
addUsedPackInfo(ci.getPackInfo());
IRteComponentGroup g = component.getParentGroup();
// collect used APIs
ICpComponent api = g.getApi();
if(api != null) {
EVersionMatchMode vmm = EVersionMatchMode.LATEST;
if(!g.isUseLatestVersion()) {
vmm = EVersionMatchMode.FIXED;
}
selectedApis.put(api, vmm);
}
}
for(Entry<ICpComponent, EVersionMatchMode> e : selectedApis.entrySet()){
ICpComponent api = e.getKey();
EVersionMatchMode versionMode = e.getValue();
ICpComponentInfo ai = null;
if(api instanceof ICpComponentInfo) {
ai = (ICpComponentInfo)api;
ai.setParent(apiInfos);
} else {
ai = new CpComponentInfo(apiInfos, api, 1);
collectFilteredFiles(ai, api);
ICpPackInfo pi = ai.getPackInfo();
if(!fUsedPackInfos.containsKey(pi.getId())) {
fUsedPackInfos.put(pi.getId(), pi);
}
}
ai.setVersionMatchMode(versionMode);
apiInfos.addChild(ai);
addUsedPackInfo(ai.getPackInfo());
}
collectGeneratedPacks();
}
protected void addUsedPackInfo(ICpPackInfo packInfo) {
if(packInfo.isGenerated())
return; // TODO: maybe in future we need to display generated packs as well as used
String packId = packInfo.getId();
if(fPackFilter.isFixed(packId)) {
packInfo.setVersionMatchMode(EVersionMatchMode.FIXED);
} else {
packInfo.setVersionMatchMode(EVersionMatchMode.LATEST);
}
if(!fUsedPackInfos.containsKey(packId)) {
fUsedPackInfos.put(packId, packInfo);
}
}
protected void collectFilteredFiles(ICpComponentInfo ci, ICpComponent c){
if(c == null) {
return;
}
Collection<? extends ICpItem> allFiles = c.getGrandChildren(CmsisConstants.FILES_TAG);
Collection<ICpItem> filtered = fComponentFilter.filterItems(allFiles); // filter by device & toolchain
filtered = fDependencySolver.filterItems(filtered); // filter by selection
ci.removeAllChildren(CmsisConstants.FILE_TAG);
createFileInfos(ci, filtered, false);
// collect generator project file to the bootstrap component
if(ci.isGenerated() || !ci.isSaved()) {
return;
}
ICpGenerator gen = c.getGenerator();
if(gen == null)
return;
createFileInfos(ci, gen.getGrandChildren(CmsisConstants.PROJECT_FILES_TAG), true);
}
protected void createFileInfos(ICpComponentInfo ci, Collection<? extends ICpItem> files, boolean generated) {
if(files == null || files.isEmpty())
return;
for(ICpItem item : files) {
if(item instanceof ICpFile) {
ICpFile f = (ICpFile)item;
ICpFileInfo fi = new CpFileInfo(ci, f);
ci.addChild(fi);
if(generated)
fi.attributes().setAttribute(CmsisConstants.GENERATED, true);
}
}
}
protected void resolveComponents(int flags) {
if(fConfigurationInfo == null) {
return;
}
// resolve components and select them
EEvaluationResult result = EEvaluationResult.FULFILLED;
EEvaluationResult res = resolveComponents(fConfigurationInfo.getGrandChildren(CmsisConstants.COMPONENTS_TAG), flags);
if(res.ordinal() < result.ordinal()) {
result = res;
}
res = resolveComponents(fConfigurationInfo.getGrandChildren(CmsisConstants.APIS_TAG), flags);
if(res.ordinal() < result.ordinal()) {
result = res;
}
evaluateComponentDependencies();
}
protected EEvaluationResult resolveComponents(Collection<? extends ICpItem> children, int flags) {
EEvaluationResult result = EEvaluationResult.FULFILLED;
if(children == null || children.isEmpty()) {
return result;
}
for(ICpItem item : children){
if(item instanceof ICpComponentInfo) { // skip doc and description items
ICpComponentInfo ci = (ICpComponentInfo) item;
if(ci.isGenerated())
continue; // Component info will be re-created
ci.setComponent(null);
ci.setEvaluationResult(EEvaluationResult.UNDEFINED);
if(ci.isApi()) {
fComponentRoot.addCpItem(ci);
} else {
fComponentRoot.addComponent(ci, flags);
}
EEvaluationResult res = ci.getEvaluationResult();
if(ci.getComponent() == null) {
ICpPackInfo pi = ci.getPackInfo();
if(resolvePack(pi) != null ){
if(fPackFilter.isExcluded(pi.getId())) {
ci.setEvaluationResult(EEvaluationResult.UNAVAILABLE_PACK);
} else {
ci.setEvaluationResult(EEvaluationResult.UNAVAILABLE);
}
}
}
if(res.ordinal() < result.ordinal()) {
result = res;
}
}
}
return result;
}
@Override
public Map<String, ICpPackInfo> getUsedPackInfos() {
return fUsedPackInfos;
}
@Override
public ICpPackFilter getPackFilter() {
return fPackFilter;
}
@Override
public boolean setPackFilter(ICpPackFilter filter) {
if(filter.equals(fPackFilter)) {
return false;
}
fPackFilter = new CpPackFilter(filter);
return true;
}
@Override
public ICpDeviceInfo getDeviceInfo() {
return fDeviceInfo;
}
@Override
public void setDeviceInfo(ICpDeviceInfo deviceInfo) {
fDeviceInfo = deviceInfo;
fConfigurationInfo.replaceChild(deviceInfo);
}
@Override
public ICpDeviceItem getDevice() {
if(fDeviceInfo != null) {
return fDeviceInfo.getDevice();
}
return null;
}
@Override
public ICpItem getToolchainInfo() {
return fToolchainInfo;
}
@Override
public IRteDeviceItem getDevices(){
if(fRteDevices == null){
fRteDevices = RteDeviceItem.createTree(fFilteredPacks);
}
return fRteDevices;
}
@Override
public IRteComponentItem getComponents() {
return fComponentRoot;
}
/**
* Updates component filter by setting new device information
*/
protected void updateComponentFilter() {
fComponentFilter = new CpConditionContext();
if(fDeviceInfo != null) {
fComponentFilter.setAttributes(fDeviceInfo.attributes().getAttributesAsMap());
// Set proper Dname attribute for condition evaluation
String deviceName = fDeviceInfo.getDeviceName();
int i = deviceName.indexOf(':');
if (i >= 0) {
deviceName = deviceName.substring(0, i);
}
fComponentFilter.setAttribute(CmsisConstants.DNAME, deviceName);
fComponentFilter.removeAttribute(CmsisConstants.URL); // this attribute is not needed for filtering
}
if(fToolchainInfo != null) {
fComponentFilter.mergeAttributes(fToolchainInfo.attributes());
}
fComponentFilter.setAttribute(CmsisConstants.THOST, Utils.getHostType());
ICpEnvironmentProvider ep = CpPlugIn.getEnvironmentProvider();
if(ep != null)
fComponentFilter.setAttribute(CmsisConstants.TENVIRONMENT, ep.getName());
fComponentFilter.resetResult();
}
/**
* Builds filtered components tree
*/
protected void collectComponents() {
fComponentRoot = new RteComponentRoot(fConfigurationInfo.getName());
// add artificial class items:
// selected device
RteSelectedDeviceClass devClass = new RteSelectedDeviceClass(fComponentRoot, fDeviceInfo);
fComponentRoot.addChild(devClass);
Collection<? extends ICpItem> children;
// process components from generated packs
if(fGeneratedPacks != null && !fGeneratedPacks.isEmpty()) {
for(ICpPack pack : fGeneratedPacks.values()){
if(pack == null)
continue;
children = pack.getGrandChildren(CmsisConstants.COMPONENTS_TAG);
collectComponents(children);
}
}
// process regular packs
if(fFilteredPacks == null || fFilteredPacks.isEmpty()) {
return;
}
// device pack has precedence, always collect its components, APIs and taxonomy first
ICpPack devicePack = null;
ICpDeviceItem device = fDeviceInfo.getDevice();
if(device != null) {
devicePack = device.getPack();
}
// first add components
if(devicePack != null) {
children = devicePack.getGrandChildren(CmsisConstants.COMPONENTS_TAG);
collectComponents(children);
}
for(ICpPack pack : fFilteredPacks ){
if(pack == devicePack) {
continue;
}
children = pack.getGrandChildren(CmsisConstants.COMPONENTS_TAG);
collectComponents(children);
}
// then add APIs and taxonomy items
if(fGeneratedPacks != null && !fGeneratedPacks.isEmpty()) {
for(ICpPack pack : fGeneratedPacks.values()){
if(pack == null)
continue;
children = pack.getGrandChildren(CmsisConstants.APIS_TAG);
collectCpItems(children);
children = pack.getGrandChildren(CmsisConstants.TAXONOMY_TAG);
collectCpItems(children);
}
}
if(devicePack != null) {
children = devicePack.getGrandChildren(CmsisConstants.APIS_TAG);
collectCpItems(children);
children = devicePack.getGrandChildren(CmsisConstants.TAXONOMY_TAG);
collectCpItems(children);
}
for(ICpPack pack : fFilteredPacks ){
if(pack == devicePack) {
continue;
}
children = pack.getGrandChildren(CmsisConstants.APIS_TAG);
collectCpItems(children);
children = pack.getGrandChildren(CmsisConstants.TAXONOMY_TAG);
collectCpItems(children);
}
// "more.." when filter is effect
if(!fPackFilter.isUseAllLatestPacks()) {
RteMoreClass more = new RteMoreClass(fComponentRoot);
fComponentRoot.addChild(more);
}
}
/**
* Adds collection members to the hierarchy
* @param children
*/
protected void collectCpItems( Collection<? extends ICpItem> children) {
if(children == null || children.isEmpty()) {
return;
}
for(ICpItem item : children){
if(item instanceof ICpTaxonomy || item instanceof ICpComponent) { // skip doc and description items
fComponentRoot.addCpItem(item);
}
}
}
/**
* Collect components from given pack
* @param pack
*/
protected void collectComponents(Collection<? extends ICpItem> children) {
if(children == null || children.isEmpty()) {
return;
}
for(ICpItem item : children){
if(item.getTag().equals(CmsisConstants.BUNDLE_TAG)){
// insert bundle implicitly since its components can be filtered out
collectComponents(item.getChildren());
} else if(item instanceof ICpComponent) { // skip doc and description items
ICpComponent c = (ICpComponent) item;
EEvaluationResult res = c.evaluate(fComponentFilter);
if(res.ordinal() < EEvaluationResult.FULFILLED.ordinal()) {
continue; // filtered out
}
fComponentRoot.addComponent(c);
}
}
}
@Override
public Collection<IRteComponent> getSelectedComponents() {
if(fComponentRoot != null) {
return fComponentRoot.getSelectedComponents(new LinkedHashSet<IRteComponent>());
}
return null;
}
@Override
public Collection<IRteComponent> getUsedComponents() {
if(fComponentRoot != null) {
return fComponentRoot.getUsedComponents(new LinkedHashSet<IRteComponent>());
}
return null;
}
@Override
public EEvaluationResult evaluateComponentDependencies() {
return fDependencySolver.evaluateDependencies();
}
@Override
public EEvaluationResult resolveComponentDependencies() {
return fDependencySolver.resolveDependencies();
}
@Override
public EEvaluationResult getEvaluationResult(IRteComponentItem item) {
return fDependencySolver.getEvaluationResult(item);
}
@Override
public Collection<? extends IRteDependencyItem> getDependencyItems() {
return fDependencySolver.getDependencyItems();
}
@Override
public EEvaluationResult getEvaluationResult() {
return fDependencySolver.getEvaluationResult();
}
@Override
public void setEvaluationResult(EEvaluationResult result) {
fDependencySolver.setEvaluationResult(result);
}
@Override
public void selectComponent(IRteComponent component, int nInstances) {
if(component == null)
return;
component.setSelected(nInstances);
}
}