]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.objmap2 / src / org / simantics / objmap / graph / impl / Mapping.java
diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java
new file mode 100644 (file)
index 0000000..ee1eb2e
--- /dev/null
@@ -0,0 +1,476 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2013 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.objmap.graph.impl;\r
+\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.util.AbstractSet;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Set;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.exception.DatabaseException;\r
+\r
+import org.simantics.objmap.backward.IBackwardMapping;\r
+import org.simantics.objmap.exceptions.MappingException;\r
+import org.simantics.objmap.forward.IForwardMapping;\r
+import org.simantics.objmap.graph.IMapping;\r
+import org.simantics.objmap.graph.IMappingListener;\r
+import org.simantics.objmap.graph.schema.ILinkType;\r
+import org.simantics.objmap.graph.schema.IMappingSchema;\r
+\r
+/**\r
+ * An implementation of IMapping. The class should not be created\r
+ * directly but using methods in Mappings.\r
+ * @see org.simantics.objmap.graph.Mappings\r
+ * @author Hannu Niemistö\r
+ */\r
+public class Mapping<Domain, Range> implements IMapping<Domain, Range> {\r
+\r
+       static Logger LOGGER = Logger.getLogger("org.simantics.objmap");\r
+       \r
+       \r
+       IMappingSchema<Domain, Range> schema;\r
+       \r
+       THashMap<Domain, Link<Domain,Range>> domain = new THashMap<Domain, Link<Domain,Range>>();\r
+       THashMap<Range, Link<Domain,Range>> range = new THashMap<Range, Link<Domain,Range>>();\r
+       ArrayList<IMappingListener> listeners = new ArrayList<IMappingListener>();\r
+\r
+       ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();\r
+       ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();\r
+\r
+       boolean disposed = false;\r
+       \r
+       boolean listensDomain; \r
+       \r
+       public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {\r
+               this.schema = schema;\r
+               this.listensDomain = listensDomain;\r
+       }\r
+       \r
+       private void removeLink(Link<Domain,Range> link) {\r
+               if(link.domainModified)\r
+                       modifiedDomainLinks.remove(link);\r
+               if(link.rangeModified)\r
+                       modifiedRangeLinks.remove(link);\r
+               link.removed = true;\r
+       }\r
+       \r
+       private void createDomain(WriteGraph g, Link<Domain,Range> link) throws MappingException {\r
+           LOGGER.info("        createDomain for " + link.rangeElement);\r
+               ILinkType<Domain,Range> type = schema.linkTypeOfRangeElement(link.rangeElement);\r
+               Domain domainElement = type.createDomainElement(g, link.rangeElement);\r
+               link.type = type;\r
+               link.domainElement = domainElement;\r
+               domain.put(domainElement, link);\r
+               type.createDomain(g, new RangeToDomain(g), domainElement, link.rangeElement);\r
+               \r
+        // TODO Should we do this only if the mapping is listening?\r
+        domainModified(link);\r
+       }\r
+       \r
+       private void createRange(ReadGraph g, Link<Domain,Range> link) throws MappingException {\r
+               ILinkType<Domain,Range> type = schema.linkTypeOfDomainElement(g, link.domainElement);           \r
+               Range rangeElement = type.createRangeElement(g, link.domainElement);\r
+               \r
+               link.type = type;\r
+               link.rangeElement = rangeElement;\r
+               range.put(rangeElement, link);\r
+               type.createRange(g, new DomainToRange(g), link.domainElement, rangeElement);\r
+       }\r
+       \r
+       Set<Domain> domainSet = new AbstractSet<Domain>() {\r
+\r
+               public boolean add(Domain e) {\r
+                       if(domain.containsKey(e))\r
+                               return false;\r
+                       Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);\r
+                       domain.put(e, link);\r
+                       modifiedDomainLinks.add(link);\r
+                       return true;\r
+               }\r
+               \r
+               public boolean contains(Object o) {\r
+                       return domain.contains(o);\r
+               }\r
+               \r
+               public boolean remove(Object o) {\r
+                       Link<Domain,Range> link = domain.remove(o);                     \r
+                       if(link == null)\r
+                               return false;\r
+                       removeLink(link);\r
+                       if(link.rangeElement != null)\r
+                               range.remove(link.rangeElement);\r
+                       return true;    \r
+               }\r
+               \r
+               @Override\r
+               public Iterator<Domain> iterator() {\r
+                   // FIXME does not implement Iterator.remove correctly\r
+                       return domain.keySet().iterator();\r
+               }\r
+\r
+               @Override\r
+               public int size() {\r
+                       return domain.size();\r
+               }\r
+               \r
+       };\r
+       \r
+       Set<Range> rangeSet = new AbstractSet<Range>() {\r
+\r
+               public boolean add(Range e) {\r
+                       if(range.containsKey(e))\r
+                               return false;\r
+                       Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);\r
+                       range.put(e, link);\r
+                       modifiedRangeLinks.add(link);\r
+                       return true;\r
+               }\r
+               \r
+               public boolean contains(Object o) {\r
+                       return range.contains(o);\r
+               }\r
+               \r
+               public boolean remove(Object o) {\r
+                       Link<Domain,Range> link = range.remove(o);                      \r
+                       if(link == null)\r
+                               return false;\r
+                       removeLink(link);\r
+                       if(link.domainElement != null)\r
+                               domain.remove(link.domainElement);\r
+                       return true;\r
+               }\r
+               \r
+               @Override\r
+               public Iterator<Range> iterator() {\r
+                   // FIXME does not implement Iterator.remove correctly\r
+                       return range.keySet().iterator();\r
+               }\r
+\r
+               @Override\r
+               public int size() {\r
+                       return range.size();\r
+               }\r
+               \r
+       };\r
+       \r
+       class DomainToRange implements IForwardMapping<Domain, Range> {\r
+\r
+               ReadGraph g;\r
+               \r
+               public DomainToRange(ReadGraph g) {\r
+                       this.g = g;\r
+               }\r
+\r
+               @Override\r
+               public Range get(Domain element)  {\r
+                       Link<Domain,Range> link = domain.get(element);\r
+                       if (link != null)\r
+                               return link.rangeElement;\r
+                       return null;\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public Range map(ReadGraph graph, Domain element)\r
+                               throws MappingException {\r
+                       Link<Domain,Range> link = domain.get(element);\r
+                       if(link == null) {\r
+                           link = new Link<Domain,Range>(null, element, null);\r
+                   link.domainModified = true;\r
+                   modifiedDomainLinks.add(link);\r
+                           domain.put(element, link);           \r
+                           createRange(g, link);       \r
+                       }\r
+                       else if(link.type == null) \r
+                               createRange(g, link);\r
+            return link.rangeElement;\r
+               }\r
+               \r
+               @Override\r
+               public Set<Domain> getDomain() {\r
+                       return domain.keySet();\r
+               }\r
+               \r
+       };\r
+       \r
+       class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {\r
+\r
+               WriteGraph g;\r
+               \r
+               public RangeToDomain(WriteGraph g) {\r
+                       super(g);\r
+                       this.g = g;\r
+               }\r
+               @Override\r
+               public Domain inverseGet(Range element) {\r
+                       \r
+                       Link<Domain,Range> link = range.get(element);\r
+                       if(link != null)\r
+                               return link.domainElement;\r
+                       return null;\r
+               }\r
+               \r
+               @Override\r
+               public Domain inverseMap(WriteGraph graph, Range element)\r
+                               throws MappingException {\r
+                       Link<Domain,Range> link = range.get(element);\r
+                       if(link == null) {\r
+                           link = new Link<Domain,Range>(null, null, element);\r
+                           link.rangeModified = true;\r
+                modifiedRangeLinks.add(link);\r
+                           range.put(element, link);\r
+                           createDomain(g, link);                              \r
+                       }\r
+                       else if(link.type == null)\r
+                               createDomain(g, link);\r
+                       return link.domainElement;\r
+               }\r
+               \r
+               \r
+               @Override\r
+               public Set<Range> getRange() {\r
+                       return range.keySet();\r
+               }\r
+       };\r
+       \r
+       @Override\r
+       public Set<Domain> getDomain() {\r
+               return domainSet;\r
+       }\r
+       \r
+       @Override\r
+       public Set<Range> getRange() {\r
+               return rangeSet;\r
+       }\r
+       \r
+       \r
+       @Override\r
+       public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {\r
+           LOGGER.info("Mapping.updateDomain");\r
+               RangeToDomain map = new RangeToDomain(g);\r
+               ArrayList<Domain> updated = new ArrayList<Domain>();\r
+               while(!modifiedRangeLinks.isEmpty()) {\r
+                   LOGGER.info("    modifiedRangeLinks.size() = " + modifiedRangeLinks.size());\r
+                   \r
+                       Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);\r
+                       link.rangeModified = false;\r
+                       /*if(link.domainModified) {\r
+                               link.domainModified = false;\r
+                               modifiedDomainLinks.remove(link);\r
+                       }*/\r
+                       \r
+                       if(link.type == null) {\r
+                               createDomain(g, link);\r
+                       }\r
+                       \r
+                       if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))\r
+                               updated.add(link.domainElement);\r
+               }       \r
+               if (listensDomain)\r
+                       updateRange(g); //FIXME: without this listening would stop. \r
+               return updated;\r
+       }\r
+       \r
+       @Override\r
+       public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {\r
+           LOGGER.info("Mapping.updateRange");\r
+               DomainToRange map = new DomainToRange(g);\r
+               ArrayList<Range> updated = new ArrayList<Range>();\r
+               while(!modifiedDomainLinks.isEmpty()) {             \r
+                   LOGGER.info("    modifiedDomainLinks.size() = " + modifiedDomainLinks.size());\r
+                   \r
+                       Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);\r
+                       link.domainModified = false;\r
+                       /*if(link.rangeModified) {\r
+                               link.rangeModified = false;\r
+                               modifiedRangeLinks.remove(link);\r
+                       }*/\r
+                       \r
+                       if(link.type == null) {\r
+                               createRange(g, link);\r
+                       }\r
+                       \r
+                       if(listensDomain) {\r
+                           RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);\r
+                           try {\r
+                    g.syncRequest(request, request);\r
+                } catch (DatabaseException e) {\r
+                    throw new MappingException(e);\r
+                }\r
+                           // TODO check if really modified\r
+                           updated.add(link.rangeElement);\r
+                       }\r
+                       else\r
+                           if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))\r
+                               updated.add(link.rangeElement);\r
+               }       \r
+               return updated;\r
+       }\r
+\r
+       @Override\r
+       public Range get(Domain domainElement) {\r
+               Link<Domain,Range> link = domain.get(domainElement);\r
+               if(link == null)\r
+                       return null;\r
+               return link.rangeElement;\r
+       }\r
+\r
+       @Override\r
+       public Domain inverseGet(Range rangeElement) {\r
+               Link<Domain,Range> link = range.get(rangeElement);\r
+               if(link == null)\r
+                       return null;\r
+               return link.domainElement;\r
+       }\r
+\r
+       @Override\r
+       public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {\r
+               getRange().add(rangeElement);\r
+               updateDomain(g);\r
+               return inverseGet(rangeElement);\r
+       }\r
+\r
+       @Override\r
+       public Range map(ReadGraph g, Domain domainElement) throws MappingException {\r
+               getDomain().add(domainElement);\r
+               updateRange(g);\r
+               return get(domainElement);\r
+       }\r
+\r
+       void domainModified(Link<Domain,Range> link) {\r
+           if(!link.domainModified) {          \r
+               synchronized(modifiedDomainLinks) {\r
+                   LOGGER.info("        domainModified for " + link.rangeElement);\r
+                link.domainModified = true;\r
+                modifiedDomainLinks.add(link);\r
+                if(modifiedDomainLinks.size() == 1) {\r
+                    for(IMappingListener listener : listeners)\r
+                        listener.domainModified();\r
+                }\r
+               }\r
+        }\r
+       }\r
+       \r
+       @Override\r
+       public void domainModified(Domain domainElement) {\r
+               Link<Domain,Range> link = domain.get(domainElement);\r
+               if(link != null)\r
+                   domainModified(link);\r
+       }\r
+\r
+       void rangeModified(Link<Domain,Range> link) {\r
+           if(!link.rangeModified) {\r
+               synchronized(modifiedRangeLinks) {\r
+                link.rangeModified = true;\r
+                modifiedRangeLinks.add(link);\r
+                if(modifiedRangeLinks.size() == 1) {\r
+                    for(IMappingListener listener : listeners)\r
+                        listener.rangeModified();\r
+                }\r
+               }\r
+        }\r
+       }\r
+       \r
+       @Override\r
+       public void rangeModified(Range rangeElement) {\r
+               Link<Domain,Range> link = range.get(rangeElement);\r
+               if(link != null)\r
+                   rangeModified(link);\r
+       }\r
+\r
+       @Override\r
+       public boolean isDomainModified() {\r
+               return !modifiedDomainLinks.isEmpty();\r
+       }\r
+\r
+       @Override\r
+       public boolean isRangeModified() {\r
+               return !modifiedRangeLinks.isEmpty();\r
+       }\r
+       \r
+       @Override\r
+       public Collection<Domain> getDomainModified() {\r
+               List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());\r
+               for (Link<Domain, Range> link : modifiedDomainLinks)\r
+                       list.add(link.domainElement);\r
+               return list;\r
+                               \r
+       }\r
+       \r
+       @Override\r
+       public Collection<Range> getRangeModified() {\r
+               List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());\r
+               for (Link<Domain, Range> link : modifiedRangeLinks)\r
+                       list.add(link.rangeElement);\r
+               return list;\r
+       }\r
+\r
+       @Override\r
+       public void addMappingListener(IMappingListener listener) {\r
+               listeners.add(listener);\r
+       }\r
+\r
+       @Override\r
+       public void removeMappingListener(IMappingListener listener) {\r
+               listeners.remove(listener);             \r
+       }\r
+\r
+       @Override\r
+       public Collection<Domain> getConflictingDomainElements() {\r
+               ArrayList<Domain> result = new ArrayList<Domain>();\r
+               if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {\r
+                       for(Link<Domain,Range> link : modifiedDomainLinks)\r
+                               if(link.rangeModified)\r
+                                       result.add(link.domainElement);\r
+               }\r
+               else {\r
+                       for(Link<Domain,Range> link : modifiedRangeLinks)\r
+                               if(link.domainModified)\r
+                                       result.add(link.domainElement);\r
+               }\r
+               return result;\r
+       }\r
+\r
+       @Override\r
+       public Collection<Range> getConflictingRangeElements() {\r
+               ArrayList<Range> result = new ArrayList<Range>();\r
+               if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {\r
+                       for(Link<Domain,Range> link : modifiedDomainLinks)\r
+                               if(link.rangeModified)\r
+                                       result.add(link.rangeElement);\r
+               }\r
+               else {\r
+                       for(Link<Domain,Range> link : modifiedRangeLinks)\r
+                               if(link.domainModified)\r
+                                       result.add(link.rangeElement);\r
+               }\r
+               return result;\r
+       }\r
+\r
+    @Override\r
+    public void dispose() {\r
+        disposed = true;\r
+    }\r
+    \r
+    public boolean isDisposed() {\r
+        return disposed;\r
+    }\r
+       \r
+}\r