]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java
Thread safety changes in objmap2
[simantics/platform.git] / bundles / org.simantics.objmap2 / src / org / simantics / objmap / graph / impl / Mapping.java
index ee1eb2e0b4bcdd48662a5a319d9d1e0d54f9d671..4820dae016b44c358fe52206d876f25a66b42fe3 100644 (file)
-/*******************************************************************************\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
+/*******************************************************************************
+ * Copyright (c) 2007, 2013 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * 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:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.objmap.graph.impl;
+
+
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.objmap.backward.IBackwardMapping;
+import org.simantics.objmap.exceptions.MappingException;
+import org.simantics.objmap.forward.IForwardMapping;
+import org.simantics.objmap.graph.IMapping;
+import org.simantics.objmap.graph.IMappingListener;
+import org.simantics.objmap.graph.schema.ILinkType;
+import org.simantics.objmap.graph.schema.IMappingSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.map.hash.THashMap;
+
+/**
+ * An implementation of IMapping. The class should not be created
+ * directly but using methods in Mappings.
+ * @see org.simantics.objmap.graph.Mappings
+ * @author Hannu Niemistö
+ */
+public class Mapping<Domain, Range> implements IMapping<Domain, Range> {
+
+       static final Logger LOGGER = LoggerFactory.getLogger(Mapping.class);
+       
+       
+       IMappingSchema<Domain, Range> schema;
+       
+       THashMap<Domain, Link<Domain,Range>> domain = new THashMap<Domain, Link<Domain,Range>>();
+       THashMap<Range, Link<Domain,Range>> range = new THashMap<Range, Link<Domain,Range>>();
+       ArrayList<IMappingListener> listeners = new ArrayList<IMappingListener>();
+
+       ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();
+       ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();
+
+       boolean disposed = false;
+       
+       boolean listensDomain; 
+       
+       public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {
+               this.schema = schema;
+               this.listensDomain = listensDomain;
+       }
+       
+       private void removeLink(Link<Domain,Range> link) {
+               if(link.domainModified)
+                       modifiedDomainLinks.remove(link);
+               if(link.rangeModified)
+                       modifiedRangeLinks.remove(link);
+               link.removed = true;
+       }
+       
+       private void createDomain(WriteGraph g, Link<Domain,Range> link) throws MappingException {
+           LOGGER.trace("        createDomain for " + link.rangeElement);
+               ILinkType<Domain,Range> type = schema.linkTypeOfRangeElement(link.rangeElement);
+               Domain domainElement = type.createDomainElement(g, link.rangeElement);
+               link.type = type;
+               link.domainElement = domainElement;
+               domain.put(domainElement, link);
+               type.createDomain(g, new RangeToDomain(), domainElement, link.rangeElement);
+               
+        // TODO Should we do this only if the mapping is listening?
+        domainModified(link);
+       }
+       
+       private void createRange(ReadGraph g, Link<Domain,Range> link) throws MappingException {
+               ILinkType<Domain,Range> type = schema.linkTypeOfDomainElement(g, link.domainElement);           
+               Range rangeElement = type.createRangeElement(g, link.domainElement);
+               
+               link.type = type;
+               link.rangeElement = rangeElement;
+               range.put(rangeElement, link);
+               type.createRange(g, new DomainToRange(), link.domainElement, rangeElement);
+       }
+       
+       Set<Domain> domainSet = new AbstractSet<Domain>() {
+
+               public boolean add(Domain e) {
+                       if(domain.containsKey(e))
+                               return false;
+                       Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);
+                       domain.put(e, link);
+                       modifiedDomainLinks.add(link);
+                       return true;
+               }
+               
+               public boolean contains(Object o) {
+                       return domain.contains(o);
+               }
+               
+               public boolean remove(Object o) {
+                       Link<Domain,Range> link = domain.remove(o);                     
+                       if(link == null)
+                               return false;
+                       removeLink(link);
+                       if(link.rangeElement != null)
+                               range.remove(link.rangeElement);
+                       return true;    
+               }
+               
+               @Override
+               public Iterator<Domain> iterator() {
+                   // FIXME does not implement Iterator.remove correctly
+                       return domain.keySet().iterator();
+               }
+
+               @Override
+               public int size() {
+                       return domain.size();
+               }
+               
+       };
+       
+       Set<Range> rangeSet = new AbstractSet<Range>() {
+
+               public boolean add(Range e) {
+                       if(range.containsKey(e))
+                               return false;
+                       Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);
+                       range.put(e, link);
+                       modifiedRangeLinks.add(link);
+                       return true;
+               }
+               
+               public boolean contains(Object o) {
+                       return range.contains(o);
+               }
+               
+               public boolean remove(Object o) {
+                       Link<Domain,Range> link = range.remove(o);                      
+                       if(link == null)
+                               return false;
+                       removeLink(link);
+                       if(link.domainElement != null)
+                               domain.remove(link.domainElement);
+                       return true;
+               }
+               
+               @Override
+               public Iterator<Range> iterator() {
+                   // FIXME does not implement Iterator.remove correctly
+                       return range.keySet().iterator();
+               }
+
+               @Override
+               public int size() {
+                       return range.size();
+               }
+               
+       };
+       
+       class DomainToRange implements IForwardMapping<Domain, Range> {
+
+               public DomainToRange() {
+               }
+
+               @Override
+               public Range get(Domain element)  {
+                       Link<Domain,Range> link = domain.get(element);
+                       if (link != null)
+                               return link.rangeElement;
+                       return null;
+                       
+               }
+               
+               @Override
+               public Range map(ReadGraph graph, Domain element)
+                               throws MappingException {
+                       Link<Domain,Range> link = domain.get(element);
+                       if(link == null) {
+                           link = new Link<Domain,Range>(null, element, null);
+                   link.domainModified = true;
+                   modifiedDomainLinks.add(link);
+                           domain.put(element, link);           
+                           createRange(graph, link);   
+                       }
+                       else if(link.type == null) 
+                               createRange(graph, link);
+            return link.rangeElement;
+               }
+               
+               @Override
+               public Set<Domain> getDomain() {
+                       return domain.keySet();
+               }
+               
+       };
+       
+       class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
+
+               public RangeToDomain() {
+                       super();
+               }
+               
+               @Override
+               public Domain inverseGet(Range element) {
+                       
+                       Link<Domain,Range> link = range.get(element);
+                       if(link != null)
+                               return link.domainElement;
+                       return null;
+               }
+               
+               @Override
+               public Domain inverseMap(WriteGraph graph, Range element)
+                               throws MappingException {
+                       Link<Domain,Range> link = range.get(element);
+                       if(link == null) {
+                           link = new Link<Domain,Range>(null, null, element);
+                           link.rangeModified = true;
+                modifiedRangeLinks.add(link);
+                           range.put(element, link);
+                           createDomain(graph, link);                          
+                       }
+                       else if(link.type == null)
+                               createDomain(graph, link);
+                       return link.domainElement;
+               }
+               
+               
+               @Override
+               public Set<Range> getRange() {
+                       return range.keySet();
+               }
+       };
+       
+       @Override
+       public Set<Domain> getDomain() {
+               return domainSet;
+       }
+       
+       @Override
+       public Set<Range> getRange() {
+               return rangeSet;
+       }
+       
+       
+       @Override
+       public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {
+           LOGGER.trace("Mapping.updateDomain");
+               RangeToDomain map = new RangeToDomain();
+               ArrayList<Domain> updated = new ArrayList<Domain>();
+               while(!modifiedRangeLinks.isEmpty()) {
+                   LOGGER.trace("    modifiedRangeLinks.size() = " + modifiedRangeLinks.size());
+                   
+                       Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);
+                       link.rangeModified = false;
+                       /*if(link.domainModified) {
+                               link.domainModified = false;
+                               modifiedDomainLinks.remove(link);
+                       }*/
+                       
+                       if(link.type == null) {
+                               createDomain(g, link);
+                       }
+                       else {
+                               if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
+                                       updated.add(link.domainElement);
+                       }
+               }       
+               if (listensDomain)
+                       updateRange(g); //FIXME: without this listening would stop. 
+               return updated;
+       }
+       
+       @Override
+       public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
+           LOGGER.trace("Mapping.updateRange");
+               DomainToRange map = new DomainToRange();
+               ArrayList<Range> updated = new ArrayList<Range>();
+               while(!modifiedDomainLinks.isEmpty()) {             
+                   LOGGER.trace("    modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
+                   
+                       Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
+                       link.domainModified = false;
+                       /*if(link.rangeModified) {
+                               link.rangeModified = false;
+                               modifiedRangeLinks.remove(link);
+                       }*/
+                       
+                       if(link.type == null) {
+                               createRange(g, link);
+                       }
+                       
+                       if(listensDomain) {
+                           RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
+                           boolean changes;
+                           try {
+                               changes = g.syncRequest(request, request) > 0;
+                } catch (DatabaseException e) {
+                    throw new MappingException(e);
+                }
+                           
+                           if (changes)
+                               updated.add(link.rangeElement);
+                       }
+                       else
+                           if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
+                               updated.add(link.rangeElement);
+               }       
+               return updated;
+       }
+
+       @Override
+       public Range get(Domain domainElement) {
+               Link<Domain,Range> link = domain.get(domainElement);
+               if(link == null)
+                       return null;
+               return link.rangeElement;
+       }
+
+       @Override
+       public Domain inverseGet(Range rangeElement) {
+               Link<Domain,Range> link = range.get(rangeElement);
+               if(link == null)
+                       return null;
+               return link.domainElement;
+       }
+
+       @Override
+       public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
+               getRange().add(rangeElement);
+               updateDomain(g);
+               return inverseGet(rangeElement);
+       }
+
+       @Override
+       public Range map(ReadGraph g, Domain domainElement) throws MappingException {
+               getDomain().add(domainElement);
+               updateRange(g);
+               return get(domainElement);
+       }
+
+       void domainModified(Link<Domain,Range> link) {
+           if(!link.domainModified) {          
+               synchronized(modifiedDomainLinks) {
+                   LOGGER.trace("        domainModified for " + link.rangeElement);
+                link.domainModified = true;
+                modifiedDomainLinks.add(link);
+                if(modifiedDomainLinks.size() == 1) {
+                    for(IMappingListener listener : listeners)
+                        listener.domainModified();
+                }
+               }
+        }
+       }
+       
+       @Override
+       public void domainModified(Domain domainElement) {
+               Link<Domain,Range> link = domain.get(domainElement);
+               if(link != null)
+                   domainModified(link);
+       }
+
+       void rangeModified(Link<Domain,Range> link) {
+           if(!link.rangeModified) {
+               synchronized(modifiedRangeLinks) {
+                link.rangeModified = true;
+                modifiedRangeLinks.add(link);
+                if(modifiedRangeLinks.size() == 1) {
+                    for(IMappingListener listener : listeners)
+                        listener.rangeModified();
+                }
+               }
+        }
+       }
+       
+       @Override
+       public void rangeModified(Range rangeElement) {
+               Link<Domain,Range> link = range.get(rangeElement);
+               if(link != null)
+                   rangeModified(link);
+       }
+
+       @Override
+       public boolean isDomainModified() {
+               return !modifiedDomainLinks.isEmpty();
+       }
+
+       @Override
+       public boolean isRangeModified() {
+               return !modifiedRangeLinks.isEmpty();
+       }
+       
+       @Override
+       public Collection<Domain> getDomainModified() {
+               List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
+               for (Link<Domain, Range> link : modifiedDomainLinks)
+                       list.add(link.domainElement);
+               return list;
+                               
+       }
+       
+       @Override
+       public Collection<Range> getRangeModified() {
+               List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
+               for (Link<Domain, Range> link : modifiedRangeLinks)
+                       list.add(link.rangeElement);
+               return list;
+       }
+
+       @Override
+       public void addMappingListener(IMappingListener listener) {
+               listeners.add(listener);
+       }
+
+       @Override
+       public void removeMappingListener(IMappingListener listener) {
+               listeners.remove(listener);             
+       }
+
+       @Override
+       public Collection<Domain> getConflictingDomainElements() {
+               ArrayList<Domain> result = new ArrayList<Domain>();
+               if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
+                       for(Link<Domain,Range> link : modifiedDomainLinks)
+                               if(link.rangeModified)
+                                       result.add(link.domainElement);
+               }
+               else {
+                       for(Link<Domain,Range> link : modifiedRangeLinks)
+                               if(link.domainModified)
+                                       result.add(link.domainElement);
+               }
+               return result;
+       }
+
+       @Override
+       public Collection<Range> getConflictingRangeElements() {
+               ArrayList<Range> result = new ArrayList<Range>();
+               if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
+                       for(Link<Domain,Range> link : modifiedDomainLinks)
+                               if(link.rangeModified)
+                                       result.add(link.rangeElement);
+               }
+               else {
+                       for(Link<Domain,Range> link : modifiedRangeLinks)
+                               if(link.domainModified)
+                                       result.add(link.rangeElement);
+               }
+               return result;
+       }
+
+    @Override
+    public void dispose() {
+        disposed = true;
+    }
+    
+    public boolean isDisposed() {
+        return disposed;
+    }
+       
+}