/******************************************************************************* * Copyright (c) 2014 Imperial College London * 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: * Raul Castro Fernandez - initial API and implementation ******************************************************************************/ package uk.ac.imperial.lsds.java2sdg.bricks.SDG; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.TreeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.java2sdg.Main; import uk.ac.imperial.lsds.java2sdg.bricks.SDGAnnotation; public class SDGBuilder { private final static Logger log = LoggerFactory.getLogger(Main.class.getCanonicalName()); private int numPartialSDG; private int partialSDGid; private TreeSet<PartialSDGWrapper> partialSDGs; public SDGBuilder(){ this.numPartialSDG = 0; this.partialSDGid = 0; this.partialSDGs = new TreeSet<PartialSDGWrapper>(PartialSDGWrapper.getComparator()); } public void addPartialSDG(List<OperatorBlock> partialSDG){ PartialSDGWrapper psw = new PartialSDGWrapper(partialSDG, partialSDGid); partialSDGid++; partialSDGs.add(psw); numPartialSDG++; } public int getNumberOfPartialSDGs(){ return partialSDGs.size(); } public List<OperatorBlock> synthetizeSDG(){ PartialSDGWrapper sdg = partialSDGs.pollLast(); while(partialSDGs.size() > 0){ PartialSDGWrapper next = partialSDGs.pollLast(); log.info("Merge "+next.getId()+" into: "+sdg.getId()); sdg = merge(sdg, next); } // Although the name is partialSDG, this is the merged, final SDG we've generated return sdg.getPartialSDG(); } private PartialSDGWrapper merge(PartialSDGWrapper a, PartialSDGWrapper b){ // First we check if both are mergeable. Is the disjoint set of states of a and b smaller than b.size? boolean areDisjoint = Collections.disjoint(a.getSet(), b.getSet()); if(areDisjoint){ // probably just put into one single partialSDG, even if they are not connected. and return something } // If they are mergeable, we put all ops in a same partialSDG a.getPartialSDG().addAll(b.getPartialSDG()); // first we merge both partialSDGs // We iterate through the ob in the partialSDG to merge for(OperatorBlock ob : b.getPartialSDG()){ int stateId = ob.getStateId(); int obId = ob.getId(); int workflowId = ob.getWorkflowId(); // If state is in a, then we merge. System.out.println("Does a contains: "+stateId+"?"); if(a.containsState(stateId)){ log.info("Merge OB: "+obId+"-"+workflowId+" because it has stateId: "+stateId); // The TE inside ob is to be merged into a. Where exactly depends on the Nature of the state if(this.isStateHandledAsPartial(stateId, a)){ // in this case we merge obId from workflowId into a as downstream as possible log.info("Merge downstream as state is handled as partial"); a = mergeDownstream(a, obId, workflowId, stateId); // remove the node, as it's been already merged a.getPartialSDG().remove(ob); } else{ // in this case we merge obId from workflowId into a as upstream as possible log.info("Merge upstream as state is not handled as partial"); a = mergeUpstream(a, obId, workflowId, stateId); // remove the node here a.getPartialSDG().remove(ob); } } } a.addStateReferencesOf(b); // first we merge both partialSDGs, keeping track of states we merge return a; } private boolean isStateHandledAsPartial(int stateId, PartialSDGWrapper x){ for(OperatorBlock ob : x.getPartialSDG()){ if(ob.getStateId() == stateId && ob.getWorkflowId() == x.getPartialSDGId()){ if(ob.getTE().getAnn() != null && (ob.getTE().getAnn().equals(SDGAnnotation.COLLECTION))){ return true; } } } return false; } private PartialSDGWrapper mergeDownstream(PartialSDGWrapper a, int obId, int workflowId, int stateId){ int psdgIdA = a.getPartialSDGId(); // First detect the ob that will host the new TE List<OperatorBlock> partialSDG = a.getPartialSDG(); ListIterator<OperatorBlock> reverseIterator = partialSDG.listIterator(partialSDG.size()); while(reverseIterator.hasPrevious()){ OperatorBlock toInspect = reverseIterator.previous(); OperatorBlock toMerge = this.getOb(obId, workflowId, a.getPartialSDG()); // We dont merge to different workflowIDs or if the target is collection and the te has local access if(toInspect.getWorkflowId() != psdgIdA || ((partialSDG.size() > 0) && toMerge.getTE().getAnn().equals(SDGAnnotation.LOCAL) && toInspect.getTE().getAnn().equals(SDGAnnotation.COLLECTION))) continue; if(toInspect.getStateId() == stateId){ // We found the ob where to merge, so we merge it, along with its id to keep track of TE connection toInspect.addTE(toMerge.getTE(), toMerge.getId(), toMerge.getWorkflowId()); // and then we change the connections for(Stream st : toMerge.getDownstreamOperator()){ //First reconfigure those downstream to point to the new ob OperatorBlock ob = this.getOb(st.getId(), st.getWorkflowId(), a.getPartialSDG()); ob.reconfigureUpstream(toMerge.getId(), toMerge.getWorkflowId(), toInspect.getId(), toInspect.getWorkflowId()); // And add newly reconfigured stream toInspect.addDownstream(st.getId(), st.getWorkflowId(), st.getType()); // Just add the downstream conn } for(Stream st : toMerge.getUpstreamOperator()){ //First reconfigure those upstream to point to the new ob OperatorBlock ob = this.getOb(st.getId(), st.getWorkflowId(), a.getPartialSDG()); ob.reconfigureDownstream(toMerge.getId(), toMerge.getWorkflowId(), toInspect.getId(), toInspect.getWorkflowId()); // And add newly reconfigured stream toInspect.addUpstream(st.getId(), st.getWorkflowId()); } // We are merging one at a time. So after merging we break break; } } return a; } private PartialSDGWrapper mergeUpstream(PartialSDGWrapper a, int obId, int workflowId, int stateId){ int psdgIdA = a.getPartialSDGId(); // First detect the ob that will host the new TE for(OperatorBlock toInspect : a.getPartialSDG()){ //OperatorBlock toInspect = reverseIterator.previous(); if(toInspect.getWorkflowId() != psdgIdA) continue; if(toInspect.getStateId() == stateId){ // We found the ob where to merge, so we merge it OperatorBlock toMerge = this.getOb(obId, workflowId, a.getPartialSDG()); toInspect.addTE(toMerge.getTE(), toMerge.getId(), toMerge.getWorkflowId()); // and then we change the connections for(Stream st : toMerge.getDownstreamOperator()){ //First reconfigure those downstream to point to the new ob OperatorBlock ob = this.getOb(st.getId(), st.getWorkflowId(), a.getPartialSDG()); ob.reconfigureUpstream(toMerge.getId(), toMerge.getWorkflowId(), toInspect.getId(), toInspect.getWorkflowId()); // And add newly reconfigured stream toInspect.addDownstream(st.getId(), st.getWorkflowId(), st.getType()); // Just add the downstream conn } for(Stream st : toMerge.getUpstreamOperator()){ //First reconfigure those upstream to point to the new ob OperatorBlock ob = this.getOb(st.getId(), st.getWorkflowId(), a.getPartialSDG()); ob.reconfigureDownstream(toMerge.getId(), toMerge.getWorkflowId(), toInspect.getId(), toInspect.getWorkflowId()); // And add newly reconfigured stream toInspect.addUpstream(st.getId(), st.getWorkflowId()); } // We are merging one at a time. So after merging we break break; } } return a; } private OperatorBlock getOb(int obId, int workflowId, List<OperatorBlock> obs){ for(int i = 0; i<obs.size(); i++){ OperatorBlock ob = obs.get(i); if(ob.getId() == obId && ob.getWorkflowId() == workflowId){ return ob; } } return null; } }