hu.bme.mit.massif.simulink.api.util.bus.BusSignalMappingCreator.java Source code

Java tutorial

Introduction

Here is the source code for hu.bme.mit.massif.simulink.api.util.bus.BusSignalMappingCreator.java

Source

/*******************************************************************************
 * 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;
        }

    }

}