/******************************************************************************* * Copyright (c) 2010-2013, Embraer S.A., Budapest University of Technology and Economics * 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: * Marton Bur, Abel Hegedus, Akos Horvath - initial API and implementation *******************************************************************************/ /** * */ package hu.bme.mit.massif.simulink.api.util.bus; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import hu.bme.mit.massif.models.simulink.util.FirstOutPortFromBusSpecificationMatch; import hu.bme.mit.massif.simulink.BusCreator; import hu.bme.mit.massif.simulink.BusSelector; import hu.bme.mit.massif.simulink.BusSignalMapping; import hu.bme.mit.massif.simulink.BusSpecification; import hu.bme.mit.massif.simulink.InPort; import hu.bme.mit.massif.simulink.OutPort; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.emf.common.util.EList; import com.google.common.base.Function; import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * * @author Abel Hegedus * */ public class BusSignalMappingCreator { private static final String DOT_REGEXP = Pattern.quote("."); private BusSignalMapper mapper; public BusSignalMappingCreator(BusSignalMapper mapper) { this.mapper = mapper; } public void createBusMapping(BusSelector selector) { checkSelectorBeforeMapping(selector); String selectorFQN = mapper.getFQNOrEmpty(selector); mapper.logDebug("Create bus mapping called for %s", selectorFQN); if (selector.getBusCreator() == null) { findCreatorAndCreateBusMapping(selector); } else { // reentrant request, do nothing mapper.logDebug("Selector mappings of %s are already set", selectorFQN); } } private void checkSelectorBeforeMapping(BusSelector selector) { checkArgument(selector != null, "Selector cannot be null!"); BusSpecification creator = selector.getBusCreator(); EList<BusSignalMapping> mappings = selector.getMappings(); checkArgument(selector.getOutports().size() <= mappings.size(), "Mappings of %s selector are incomplete!", selector); for (BusSignalMapping mapping : mappings) { checkState(!Strings.isNullOrEmpty(mapping.getMappingPath()), "Mapping path of mapping %s cannot be null or empty!", mapping); if (creator != null) { checkState(mapping.getMappingFrom() != null, "Mapping %s is incomplete, FROM outport not set!", mapping); } checkState(mapping.getMappingTo() != null, "Mapping %s is incomplete, TO outport not set!", mapping); checkState(selector.getOutports().contains(mapping.getMappingTo()), "TO outport of mapping %s is not outport of selector!", mapping); } } private void findCreatorAndCreateBusMapping(BusSelector selector) { SpecifiableOriginatingOutPort origin = findBusCreatorOfIncomigBus(selector); if (origin.specification == null) { handleIncompleteMappings(origin.outPort, selector.getMappings()); } else { createBusMappingUsingCreator(selector, origin.specification); } mapper.logDebug("Created bus mapping for %s", mapper.getFQNOrEmpty(selector)); } private SpecifiableOriginatingOutPort findBusCreatorOfIncomigBus(BusSelector selector) { SpecifiableOriginatingOutPort origin = getOriginatingOutPortForBusSelector(selector); BusSpecification previousSpec = origin.specification; if (previousSpec != null) { origin = findBusCreatorForOriginatingBusSpecification(origin); } return origin; } private SpecifiableOriginatingOutPort getOriginatingOutPortForBusSelector(BusSelector selector) { // outport before bus selector is always determined, setting not required SpecifiableOriginatingOutPort result = getOriginatingOutPortForBusSpecfication(selector, null); return result; } private SpecifiableOriginatingOutPort getOriginatingOutPortForBusSpecfication(BusSpecification specification, OutPort outPort) { OutPort firstOutPort = findOriginatingOutPortForSpecification(specification, outPort); BusSpecification prevSpec = null; if (firstOutPort.getContainer() instanceof BusSpecification) { prevSpec = (BusSpecification) firstOutPort.getContainer(); } SpecifiableOriginatingOutPort result = new SpecifiableOriginatingOutPort(prevSpec, firstOutPort); return result; } private OutPort findOriginatingOutPortForSpecification(BusSpecification specification, OutPort outPortBeforeSpecification) { Collection<FirstOutPortFromBusSpecificationMatch> matches = mapper.getFirstOutPortFromBusSpecificationMatcher() .getAllMatches(null, specification, outPortBeforeSpecification); checkState(matches.size() == 1, "Invalid model, backward navigation is not deterministic!"); FirstOutPortFromBusSpecificationMatch match = Iterables.getOnlyElement(matches); OutPort firstOutPort = (OutPort) match.getOutPort(); return firstOutPort; } private SpecifiableOriginatingOutPort findBusCreatorForOriginatingBusSpecification(SpecifiableOriginatingOutPort origin) { BusSpecification busSpecification = origin.specification; OutPort previousSpecOutPort = origin.outPort; SpecifiableOriginatingOutPort creator = null; if (busSpecification instanceof BusSelector) { BusSelector previousBusSelector = (BusSelector) busSpecification; if (previousBusSelector.getBusCreator() == null) { // not set -> push current exploration to "stack" and create bus mapping of previous selector createBusMapping(previousBusSelector); } creator = findBusCreatorThroughBusSelector(busSpecification, previousSpecOutPort, previousBusSelector); } else if (busSpecification instanceof BusCreator) { creator = new SpecifiableOriginatingOutPort(busSpecification, previousSpecOutPort); } else { throw new IllegalStateException( "Previous bus specification cannot be other type of elements! But encountered: " + busSpecification); } BusSpecification specification = creator.specification; mapper.logDebug(String.format("Creator of bus is %s", mapper.getFQNOrEmpty(specification))); return creator; } private SpecifiableOriginatingOutPort findBusCreatorThroughBusSelector(BusSpecification busSpecification, OutPort previousSpecOutPort, BusSelector previousBusSelector) { SpecifiableOriginatingOutPort creator = null; if (previousBusSelector.isOutputAsBus()) { creator = new SpecifiableOriginatingOutPort(busSpecification, previousSpecOutPort); } else { // bus creator set and mappings done -> use mapping to find corresponding outport and continue for (BusSignalMapping m : previousBusSelector.getMappings()) { if (previousSpecOutPort.equals(m.getMappingTo())) { OutPort mappingFrom = m.getMappingFrom(); // go backwards creator = findBusCreatorOfBusSignal(previousBusSelector.getBusCreator(), mappingFrom); } } } return creator; } private SpecifiableOriginatingOutPort findBusCreatorOfBusSignal(BusSpecification creator, OutPort outPort) { SpecifiableOriginatingOutPort origin = getOriginatingOutPortForBusSpecfication(creator, outPort); if (origin.specification != null) { origin = findBusCreatorForOriginatingBusSpecification(origin); } return origin; } private void handleIncompleteMappings(OutPort firstOutPort, Iterable<BusSignalMapping> mappings) { for (BusSignalMapping busSignalMapping : mappings) { busSignalMapping.setIncomplete(true); busSignalMapping.setMappingFrom(firstOutPort); mapper.logDebug("Setting incomplete mapping: %s, port: %s", busSignalMapping.getMappingPath(), mapper.getFQNOrEmpty(firstOutPort)); } } private void createBusMappingUsingCreator(BusSelector selector, BusSpecification creator) { Map<String, FragmentResolution> fragmentResolutionMap = initializeFragmentResolutionMap(selector); resolveMappingsInFragmentResolutionMap(creator, fragmentResolutionMap); selector.setBusCreator(creator); } private Map<String, FragmentResolution> initializeFragmentResolutionMap(BusSelector selector) { Map<String, FragmentResolution> resolutionMap = Maps.newHashMap(); for (BusSignalMapping mapping : selector.getMappings()) { // find mappingFrom for each mapping by path String mappingPath = mapping.getMappingPath(); OutPort mappingTo = mapping.getMappingTo(); List<String> fragments = splitPathToFragments(mappingPath); mapper.logDebug("Storing fragments: %s, port: %s", fragments, mapper.getFQNOrEmpty(mappingTo)); storeFragmentsInMap(resolutionMap, mapping, mappingTo, fragments); } return resolutionMap; } private List<String> splitPathToFragments(String mappingPath) { // split path by dot, could be new StringTokenizer(mappingPath,"."); List<String> fragments = Lists.newArrayList(mappingPath.split(DOT_REGEXP)); return fragments; } private void storeFragmentsInMap(Map<String, FragmentResolution> resolutionMap, BusSignalMapping mapping, OutPort outPort, List<String> fragments) { // first fragment identifies common mappings String firstFrag = fragments.remove(0); FragmentResolution fragStore = resolutionMap.get(firstFrag); if (fragStore == null) { fragStore = new FragmentResolution(outPort); resolutionMap.put(firstFrag, fragStore); } // put rest of fragments with mapping to track resolution fragStore.fragmentMap.put(mapping, fragments); } private void resolveMappingsInFragmentResolutionMap(BusSpecification creator, Map<String, FragmentResolution> resolutionMap) { updateResolutionMapForFirstSegments(creator, resolutionMap); for (FragmentResolution store : resolutionMap.values()) { findOutPortForMappings(creator, store); } } private void updateResolutionMapForFirstSegments(BusSpecification specification, Map<String, FragmentResolution> resolutionMap) { if (specification instanceof BusCreator) { // incoming line names are used for (InPort inportOfCreator : specification.getInports()) { String collisionFreeLineName = mapper.getCollisionFreeLineName(inportOfCreator); OutPort connectedOutPort = mapper.getConnectedOutPort(inportOfCreator); setOutPortInResolutionMap(resolutionMap, collisionFreeLineName, connectedOutPort, false); } } else if (specification instanceof BusSelector) { BusSelector busSelector = (BusSelector) specification; // use mapping (tricky): find outport, look at name, etc. // we know that signals with the same name are not allowed to be selected into a bus Set<String> names = Sets.newHashSet(); for (BusSignalMapping mapping : busSelector.getMappings()) { List<String> fragments = splitPathToFragments(mapping.getMappingPath()); String lastFrag = Iterables.getLast(fragments); checkState(!names.contains(lastFrag), "Duplicate signal %s name in bus selector", lastFrag); names.add(lastFrag); setOutPortInResolutionMap(resolutionMap, lastFrag, mapping.getMappingFrom(), mapping.isIncomplete()); } } } private void setOutPortInResolutionMap(Map<String, FragmentResolution> resolutionMap, String collisionFreeLineName, OutPort connectedOutPort, boolean incomplete) { FragmentResolution fragStore = resolutionMap.get(collisionFreeLineName); if (fragStore != null) { fragStore.outPort = connectedOutPort; if (incomplete) { for (BusSignalMapping mapping : fragStore.fragmentMap.keySet()) { mapping.setIncomplete(true); mapper.logDebug("Setting incomplete mapping: %s, port: %s", mapping.getMappingPath(), mapper.getFQNOrEmpty(connectedOutPort)); } } mapper.logDebug("Found outport for fragment %s, port: %s", collisionFreeLineName, mapper.getFQNOrEmpty(connectedOutPort)); } } private void findOutPortForMappings(BusSpecification creator, FragmentResolution resolution) { OutPort outPort = resolution.outPort; Map<String, FragmentResolution> storeMap = resolveNextFragment(resolution, outPort); if (!storeMap.isEmpty()) { // unresolved mappings need further navigation SpecifiableOriginatingOutPort origin = findBusCreatorOfBusSignal(creator, outPort); BusSpecification prevBusSpec = origin.specification; if (prevBusSpec != null) { resolveMappingsInFragmentResolutionMap(prevBusSpec, storeMap); } else { Iterable<BusSignalMapping> allMappingsInResolutionMap = getAllMappingInResolutionMap(storeMap); handleIncompleteMappings(origin.outPort, allMappingsInResolutionMap); } } } private Map<String, FragmentResolution> resolveNextFragment(FragmentResolution resolution, OutPort outPort) { Map<String, FragmentResolution> storeMap = Maps.newHashMap(); for (Entry<BusSignalMapping, List<String>> entry : resolution.fragmentMap.entrySet()) { List<String> fragments = entry.getValue(); BusSignalMapping mapping = entry.getKey(); if (fragments.isEmpty()) { mapping.setMappingFrom(outPort); mapper.logDebug("Found mapping %s, port: %s", mapping.getMappingPath(), mapper.getFQNOrEmpty(outPort)); } else { storeFragmentsInMap(storeMap, mapping, outPort, fragments); } } return storeMap; } private Iterable<BusSignalMapping> getAllMappingInResolutionMap(Map<String, FragmentResolution> resolutionMap) { Function<FragmentResolution, Set<BusSignalMapping>> resolutionToMappings = new Function<FragmentResolution, Set<BusSignalMapping>>() { @Override public Set<BusSignalMapping> apply(FragmentResolution input) { return input.fragmentMap.keySet(); } }; Iterable<Set<BusSignalMapping>> resolutionMapToMappings = Iterables.transform(resolutionMap.values(), resolutionToMappings); Iterable<BusSignalMapping> allMappingsInResolutionMap = Iterables.concat(resolutionMapToMappings); return allMappingsInResolutionMap; } /** * Each {@link FragmentResolution} is responsible for storing the partial fragments that have to be resolved with * regards to the outport. * * @author Abel Hegedus * */ private static class FragmentResolution { OutPort outPort; Map<BusSignalMapping, List<String>> fragmentMap = Maps.newHashMap(); public FragmentResolution(OutPort outPort) { this.outPort = outPort; } } private static class SpecifiableOriginatingOutPort { BusSpecification specification; OutPort outPort; public SpecifiableOriginatingOutPort(BusSpecification specification, OutPort outPort) { this.specification = specification; this.outPort = outPort; } } }