X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.interop.mapping%2Fsrc%2Forg%2Fsimantics%2Finterop%2Fmapping%2FMapper.java;fp=org.simantics.interop.mapping%2Fsrc%2Forg%2Fsimantics%2Finterop%2Fmapping%2FMapper.java;h=6f700d3bc7a18e05badca5635de17a524693711e;hb=bd7152b9aaac10befa93c6c1fd9e4f1f9fd15c4a;hp=0000000000000000000000000000000000000000;hpb=c2f8c678f5160b4c3fd235025eaddc5735099942;p=simantics%2Finterop.git diff --git a/org.simantics.interop.mapping/src/org/simantics/interop/mapping/Mapper.java b/org.simantics.interop.mapping/src/org/simantics/interop/mapping/Mapper.java new file mode 100644 index 0000000..6f700d3 --- /dev/null +++ b/org.simantics.interop.mapping/src/org/simantics/interop/mapping/Mapper.java @@ -0,0 +1,772 @@ +package org.simantics.interop.mapping; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.VirtualGraph; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.common.request.WriteResultRequest; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.layer0.util.SessionGarbageCollection; +import org.simantics.db.request.Read; +import org.simantics.interop.mapping.data.GraphNode; +import org.simantics.interop.mapping.data.Identifiable; +import org.simantics.interop.mapping.data.Link; +import org.simantics.interop.mapping.data.ResourceIdentifiable; +import org.simantics.ui.jobs.SessionGarbageCollectorJob; +import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.datastructures.Pair; + +/** + * + * @author Marko Luukkainen + * + */ +public class Mapper { + + public static final boolean USE_SPLIT_TRANSACTIONS = false; // Split transactions + public static int OBJECTS_PER_TRANSACTION = 5000; // number of objects handled per transaction (split mode) + private static boolean SLEEP_BETWEEN_WRITES = false; // sleep between transactions (split mode) + private static int SLEEP_TIME = 10; // time to sleep (ms) + private static boolean COLLECT_BETWEEN_WRITES = false; // Run SessionGC between split transactions + private static boolean COLLECT_WITHIN_TRANSACTIONS = true; // Run Collect within transactions (both modes) + public static int OBJECTS_BEFORE_COLLECT = 5000; // number of objects that are handled before collect (non-split mode) + + private List initializedRules = new ArrayList(); + private List>> generationRules; + private List> globalModificationRules; + private List>> modificationRules; + private List> connectionRules; + + int maxGenPass = 0; + + public Mapper() { + long maxMemory = Runtime.getRuntime().maxMemory(); + maxMemory /= (1024*1024); // Convert to MB; + int use = 290; // Estimated memory usage of the system + int freeMem = (int)maxMemory-use; // Free memory for mappings + OBJECTS_BEFORE_COLLECT = (freeMem * freeMem) / 1000; //600M heap -> 84, 3000M heap -> 5645 + if (OBJECTS_BEFORE_COLLECT < 2) + OBJECTS_BEFORE_COLLECT = 2; + OBJECTS_PER_TRANSACTION = OBJECTS_BEFORE_COLLECT; + + + generationRules = new ArrayList>>(); + modificationRules = new ArrayList>>(); + globalModificationRules = new ArrayList>(); + connectionRules = new ArrayList>(); + } + + public void addRule(int pass, IdentificationRule idRule, GenerationRule genRule) { + if (idRule == null || genRule == null) throw new NullPointerException(); + generationRules.add(new Pair>(idRule, new Pair(pass,genRule))); + maxGenPass = Math.max(maxGenPass, pass); + if (genRule instanceof InitializedRule) + initializedRules.add((InitializedRule)genRule); + } + + public void addRule(IdentificationRule idRule, MappingRule mappingRule) { + addRule(0,idRule,mappingRule); + } + + public void addRule(int pass, IdentificationRule idRule, MappingRule mappingRule) { + if (idRule == null || mappingRule == null) throw new NullPointerException(); + if (mappingRule instanceof ModificationRule) { + while (pass >= modificationRules.size()) { + modificationRules.add(new ArrayList>()); + } + List> priList = modificationRules.get(pass); + priList.add(new Pair(idRule, (ModificationRule)mappingRule)); + } + if (mappingRule instanceof GenerationRule) + generationRules.add(new Pair>(idRule, new Pair(pass,(GenerationRule)mappingRule))); + if (mappingRule instanceof InitializedRule) + initializedRules.add((InitializedRule)mappingRule); + } + + public void addRule(IdentificationRule idRule, ModificationRule... modRules) { + addRule(0, idRule, modRules); + } + + public void addRule(int pass, IdentificationRule idRule, ModificationRule... modRules) { + if (idRule == null) throw new NullPointerException(); + + while (pass >= modificationRules.size()) { + modificationRules.add(new ArrayList>()); + } + List> priList = modificationRules.get(pass); + + for (ModificationRule modRule : modRules){ + if (modRule == null) throw new NullPointerException(); + priList.add(new Pair(idRule, modRule)); + if (modRule instanceof InitializedRule) + initializedRules.add((InitializedRule)modRule); + } + } + public void addRule(ModificationRule modRule) { + addRule(0, modRule); + } + + public void addRule(int pass, ModificationRule modRule) { + if (modRule == null) throw new NullPointerException(); + while (pass >= globalModificationRules.size()) { + globalModificationRules.add(new ArrayList()); + } + List priList = globalModificationRules.get(pass); + priList.add(modRule); + if (modRule instanceof InitializedRule) + initializedRules.add((InitializedRule)modRule); + } + + public void addRule(ConnectionIdentificationRule idRule, ConnectionGenerationRule genRule) { + if (idRule == null || genRule == null) throw new NullPointerException(); + connectionRules.add(new Pair(idRule, genRule)); + if (genRule instanceof InitializedRule) + initializedRules.add((InitializedRule)genRule); + } + + /** + * Runs the mapping procedure. Disposes nodes after mapping is done. + * @param g + * @param model + * @param nodes + * @param monitor + * @throws Exception + */ + public void map(WriteGraph g, Resource model, Collection> nodes, IProgressMonitor monitor) throws Exception { + startMapping(null); + try { + if (monitor == null) + monitor = new NullProgressMonitor(); + + for (InitializedRule rule : initializedRules) + rule.initialize(g, model); + + applyModifications(g, nodes, monitor); + monitor.worked(1); + applyGenerations(g,nodes,monitor); + monitor.worked(1); + applyConnections(g,nodes,monitor); + monitor.worked(1); + } finally { + MappingTools.disposeNodes(nodes); + endMapping(); + } + + } + + /** + * Runs the mapping procedure. Disposes nodes after mapping is done. + * @param session + * @param model + * @param nodes + * @param monitor + * @throws Exception + */ + public void map(Session session, Resource model, Collection> nodes, IProgressMonitor monitor) throws Exception { + map(session, model, null, nodes, monitor); + } + + /** + * Runs the mapping procedure. Disposes nodes after mapping is done. + * @param session + * @param model + * @param vg + * @param nodes + * @param monitor + * @throws Exception + */ + public void map(Session session, Resource model, VirtualGraph vg, Collection> nodes, IProgressMonitor monitor) throws Exception { + startMapping(vg); + try { + long time = System.currentTimeMillis(); + if (monitor == null) + monitor = new NullProgressMonitor(); + + initializeRules(session, vg, model); + if (USE_SPLIT_TRANSACTIONS) { + applyModifications(session, nodes, monitor); + monitor.worked(1); + applyGenerations(session, vg, nodes, monitor); + monitor.worked(1); + applyConnections(session, vg, nodes, monitor); + monitor.worked(1); + } else { + applyModifications2(session, nodes, monitor); + monitor.worked(1); + applyGenerations2(session, vg, nodes, monitor); + monitor.worked(1); + applyConnections2(session, vg, nodes, monitor); + monitor.worked(1); + } + long time2 = System.currentTimeMillis(); + System.out.println("Mapping took " + ((time2-time)/1000) + " seconds"); + } finally { + MappingTools.disposeNodes(nodes); + if (COLLECT_BETWEEN_WRITES) { + + SessionGarbageCollection.gc(null, session, true, null); + } + endMapping(); + } + + } + + + //private boolean autosaveEndabled = false; + private VirtualGraph vg; + + protected void startMapping(VirtualGraph vg) { + SessionGarbageCollectorJob.getInstance().setEnabled(false); + //autosaveEndabled = AutosaveCommands.getInstance().isEnabled(); + //AutosaveCommands.getInstance().setEnabled(false); + this.vg = vg; + } + + protected void endMapping() { + SessionGarbageCollectorJob.getInstance().setEnabled(true).scheduleAfterQuietTime(); + // AutosaveCommands.getInstance().setEnabled(autosaveEndabled); + vg = null; + } + + private void applyModifications(ReadGraph g, Collection> nodes, IProgressMonitor monitor) throws Exception { + + // Apply global modification rules first + + int passCount = Math.max(globalModificationRules.size(),modificationRules.size()); + + for (int pass = 0; pass < passCount; pass++) { + if (globalModificationRules.size() > pass) { + int count = 0; + List modRules = globalModificationRules.get(pass); + int size = modRules.size(); + monitor.subTask("Running global modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + for (ModificationRule r : modRules) { + Collection> ruleModified = r.modify(g, nodes); + if (ruleModified == null) + continue; + for (GraphNode m : ruleModified) { + if (m.isDisposed()) { + nodes.remove(m); + } + else if (!nodes.contains(m)) { + nodes.add(m); + } + } + monitor.subTask("Running global modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(g); + } + } if (modificationRules.size() > pass) { + int count = 0; + List> modRules = modificationRules.get(pass); + int size = modRules.size(); + monitor.subTask("Running object modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + // Apply per object modification rules + for (Pair modRule : modRules) { + Collection> ruleModified = new ArrayList>(); + for (GraphNode n : nodes) { + applyModifications(g, n, modRule, ruleModified); + if (COLLECT_WITHIN_TRANSACTIONS) + collect2(g); + } + + for (GraphNode m : ruleModified) { + if (m.isDisposed()) { + nodes.remove(m); + } + else if (!nodes.contains(m)) { + nodes.add(m); + } + } + monitor.subTask("Running object modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + + } + } + } + + } + + + + private void applyGenerations(WriteGraph graph, Collection> nodes, IProgressMonitor monitor) throws Exception { + + int size = nodes.size(); + int count = 0; + monitor.subTask("Assigning generation rules ("+ count + "/" + size + ")"); + // populate generation rules + for (GraphNode n : nodes) { + + for (Pair> r : generationRules) { + if (r.first.matches(graph, n)) { + MappingTools.assignGenerationRule(n, r.second.first,r.second.second); + } + } + monitor.subTask("Assigning generation rules ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect2(graph); + } + + count = 0; + monitor.subTask("Generating objects ("+ count + "/" + size + ")"); + + // apply generation rules. + //WriteWrapper g = new WriteWrapper(graph); + + //Collection usedRules = new ArrayList(); + for (int stage = 0; stage <= maxGenPass; stage++) { + count = 0; + for (GraphNode n : nodes) { + + MapList priRules = n.getHint(MappingHints.KEY_GENERATION_RULES); + List rules = priRules.getValues(stage); + for (GenerationRule r : rules) { + r.generate(graph, n); + } + + monitor.subTask("Generating objects, stage " + stage + " : ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect2(graph); + } + } + } + + private void applyConnections(WriteGraph g, Collection> nodes, IProgressMonitor monitor) throws Exception { + int size = nodes.size(); + int count = 0; + + monitor.subTask("Generating connections ("+ count + "/" + size + ")"); + + for (GraphNode node : nodes) { + applyConnections(g, node); + monitor.subTask("Generating connections ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect2(g); + } + } + + + + protected String getName(ReadGraph g, Identifiable res) throws DatabaseException { + if (res instanceof ResourceIdentifiable) + return NameUtils.getSafeName(g, ((ResourceIdentifiable)res).getResource()); + else + return res.toString(); + } + + public class WriteWrapper extends WriteGraphProxy { + + + private Collection createdResources = new ArrayList(); + + public WriteWrapper(WriteGraph graph) { + super(graph); + } + + public Collection getCreatedResources() { + return createdResources; + } + + public void clearCreated() { + createdResources = new ArrayList(); + } + + @Override + public Resource newResource() throws ServiceException { + Resource res = graph.newResource(); + createdResources.add(res); + return res; + } + + @Override + public Resource newResource(long clusterId) throws ServiceException { + Resource res = graph.newResource(clusterId); + createdResources.add(res); + return res; + } + + + } + + private void initializeRules(Session session, VirtualGraph vg, final Resource model ) throws DatabaseException{ + session.syncRequest(new WriteRequest(vg) { + @Override + public void perform(WriteGraph g) throws DatabaseException { + for (InitializedRule rule : initializedRules) + rule.initialize(g, model); + + } + }); + + } + + private void collect(ReadGraph g) throws DatabaseException { + if (vg != null) + return; + SessionGarbageCollection.gc(g, 0, -1); + } + + int collect = 0; + + private void collect2(ReadGraph g) throws DatabaseException { + if (vg != null) + return; + + if (collect == OBJECTS_BEFORE_COLLECT) { + SessionGarbageCollection.gc(g, 0, -1); + collect = 0; + } else { + collect++; + } + } + + private void applyModifications(Session session, final Collection> nodes, final IProgressMonitor monitor) throws Exception { + + + + int passCount = Math.max(globalModificationRules.size(),modificationRules.size()); + + for (int pass = 0; pass pass) { + int count = 0; + List modRules = globalModificationRules.get(pass); + int size = modRules.size(); + monitor.subTask("Running global modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + for (final ModificationRule r : modRules) { + session.syncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph g) throws DatabaseException { + try { + Collection> ruleModified = r.modify(g, nodes); + + if (ruleModified == null) + return; + for (GraphNode m : ruleModified) { + if (m.isDisposed()) { + nodes.remove(m); + } + else if (!nodes.contains(m)) { + nodes.add(m); + } + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(g); + } catch (Exception e) { + throw new DatabaseException(e); + } + + } + }); + //SessionGarbageCollection.gc(null, session, true, null); + monitor.subTask("Running global modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + } + } + if (modificationRules.size() > pass) { + int count = 0; + List> modRules = modificationRules.get(pass); + int size = modRules.size(); + monitor.subTask("Running object modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + + // Apply per object modification rules + for (final Pair modRule : modRules) { + final Iterator> iter = nodes.iterator(); + final Collection> ruleModified = new ArrayList>(); + while (iter.hasNext()) { + session.syncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph g) throws DatabaseException { + try { + + int j = 0; + //for (GraphNode n : nodes) { + while (iter.hasNext() && j < OBJECTS_PER_TRANSACTION) { + GraphNode n = iter.next(); + applyModifications(g, n, modRule, ruleModified); + j++; + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(g); + + } catch (Exception e) { + throw new DatabaseException(e); + } + + } + }); + } + for (GraphNode m : ruleModified) { + if (m.isDisposed()) { + nodes.remove(m); + } + else if (!nodes.contains(m)) { + nodes.add(m); + } + } + ruleModified.clear(); + + //SessionGarbageCollection.gc(null, session, true, null); + monitor.subTask("Running object modification rules: pass (" + (pass+1) + "/" + passCount + "), rule ("+ (++count) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + } + } + } + + } + + private void applyModifications2(Session session, final Collection> nodes, final IProgressMonitor monitor) throws Exception { + session.syncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph g) throws DatabaseException { + try { + applyModifications(g, nodes, monitor); + } catch (Exception e) { + throw new DatabaseException(e); + } + + } + }); + } + + private void applyGenerations(Session session, VirtualGraph vg, Collection> nodes, IProgressMonitor monitor) throws Exception { + + int size = nodes.size(); + int count = 0; + monitor.subTask("Assigning generation rules ("+ count + "/" + size + ")"); + // populate generation rules + + { + final Iterator> iter = nodes.iterator(); + while (iter.hasNext()) { + int c = session.syncRequest(new Read() { + + @Override + public Integer perform(ReadGraph graph) + throws DatabaseException { + int j = 0; + while (iter.hasNext() && j < OBJECTS_PER_TRANSACTION) { + GraphNode n = iter.next(); + for (Pair> r : generationRules) { + if (r.first.matches(graph, n)) { + MapList rules = n.getHint(MappingHints.KEY_GENERATION_RULES); + if (rules == null) { + rules = new MapList(); + } + rules.add(r.second.first,r.second.second); + n.setHint(MappingHints.KEY_GENERATION_RULES, rules); + } + } + j++; + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(graph); + return j; + + } + }); + collect(session); + monitor.subTask("Assigning generation rules ("+ (count+=c) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + sleep(); + } + } + + count = 0; + monitor.subTask("Generating objects ("+ (count) + "/" + size + ")"); + + // apply generation rules. + + { + for (int stage = 0; stage <= maxGenPass; stage++) { + final int fStage = stage; + count = 0; + final Iterator> iter = nodes.iterator(); + while (iter.hasNext()) { + int c = session.syncRequest(new WriteResultRequest(vg) { + + @Override + public Integer perform(WriteGraph graph) throws DatabaseException { + int j = 0; + try { + while (iter.hasNext() && j < OBJECTS_PER_TRANSACTION) { + GraphNode n = iter.next(); + + MapList priRules = n.getHint(MappingHints.KEY_GENERATION_RULES); + if (priRules == null) { + j++; + continue; + } + final List rules = priRules.getValues(fStage); + + if (fStage == 0 && rules.size() == 0) + System.out.println(); + for (GenerationRule r : rules) { + r.generate(graph, n); + + } + + + j++; + + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(graph); + return j; + + } catch (Exception e) { + throw new DatabaseException(e); + } + }}); + collect(session); + monitor.subTask("Generating objects, stage " + stage + " : ("+ (count+=c) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + sleep(); + } + } + } + } + + private void applyGenerations2(Session session, VirtualGraph vg, final Collection> nodes, final IProgressMonitor monitor) throws Exception { + session.syncRequest(new WriteRequest(vg) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + try { + applyGenerations(graph, nodes, monitor); + } catch (Exception e) { + throw new DatabaseException(e); + } + + } + }); + } + + private void collect(Session session) { + if (COLLECT_BETWEEN_WRITES) + SessionGarbageCollection.gc(null, session, true, null); + } + + + private void sleep() { + if (SLEEP_BETWEEN_WRITES) { + try { + Thread.sleep(SLEEP_TIME); + } catch (InterruptedException e) { + + } + } + } + + private void applyConnections(Session session, VirtualGraph vg, Collection> nodes, IProgressMonitor monitor) throws Exception { + int size = nodes.size(); + int count = 0; + + monitor.subTask("Generating connections ("+ count + "/" + size + ")"); + + final Iterator> iter = nodes.iterator(); + while (iter.hasNext()) { + int c = session.syncRequest(new WriteResultRequest(vg) { + + @Override + public Integer perform(WriteGraph g) throws DatabaseException { + int j = 0; + try { + while (iter.hasNext() && j < OBJECTS_PER_TRANSACTION) { + GraphNode node = iter.next(); + applyConnections(g, node); + j++; + } + if (COLLECT_WITHIN_TRANSACTIONS) + collect(g); + return j; + + } catch (Exception e) { + throw new DatabaseException(e); + } + } + }); + collect(session); + monitor.subTask("Generating connections ("+ (count+=c) + "/" + size + ")"); + if(monitor.isCanceled()) { + throw new CancelException("Cancel requested."); + } + sleep(); + } + } + + private void applyConnections2(Session session, VirtualGraph vg, final Collection> nodes, final IProgressMonitor monitor) throws Exception { + session.syncRequest(new WriteRequest(vg) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + try { + applyConnections(graph, nodes, monitor); + } catch (Exception e) { + throw new DatabaseException(e); + } + + } + }); + } + + private void applyModifications(ReadGraph g, GraphNode n, Pair modRule, Collection> ruleModified) throws Exception { + if (!n.isDisposed() && modRule.first.matches(g, n)) { // we have to check + Collection> perRule = new ArrayList>(); + perRule.add(n); + ruleModified.addAll(modRule.second.modify(g, perRule)); + } + } + + private void applyConnections(WriteGraph g, GraphNode node) throws Exception { + for (Link link : node.getLinks()) { + if (link.isMain()) { + + for (Pair r : connectionRules) { + if (r.first.mathces(g, node, link.to(), link)) { + Logger.defaultLogInfo("Connecting " + getName(g, node.getData()) + " to " + getName(g, link.to().getData()) + " " + link.getName() + "/"+link.getInverseName()); + r.second.generate(g, node, link.to(), link); + break; + } + } + } + } + } +}