1 /*******************************************************************************
2 * Copyright (c) 2007, 2013 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.objmap.graph.impl;
15 import gnu.trove.map.hash.THashMap;
17 import java.util.AbstractSet;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.List;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.simantics.db.ReadGraph;
27 import org.simantics.db.WriteGraph;
28 import org.simantics.db.exception.DatabaseException;
30 import org.simantics.objmap.backward.IBackwardMapping;
31 import org.simantics.objmap.exceptions.MappingException;
32 import org.simantics.objmap.forward.IForwardMapping;
33 import org.simantics.objmap.graph.IMapping;
34 import org.simantics.objmap.graph.IMappingListener;
35 import org.simantics.objmap.graph.schema.ILinkType;
36 import org.simantics.objmap.graph.schema.IMappingSchema;
39 * An implementation of IMapping. The class should not be created
40 * directly but using methods in Mappings.
41 * @see org.simantics.objmap.graph.Mappings
42 * @author Hannu Niemistö
44 public class Mapping<Domain, Range> implements IMapping<Domain, Range> {
46 static final Logger LOGGER = LoggerFactory.getLogger(Mapping.class);
49 IMappingSchema<Domain, Range> schema;
51 THashMap<Domain, Link<Domain,Range>> domain = new THashMap<Domain, Link<Domain,Range>>();
52 THashMap<Range, Link<Domain,Range>> range = new THashMap<Range, Link<Domain,Range>>();
53 ArrayList<IMappingListener> listeners = new ArrayList<IMappingListener>();
55 ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();
56 ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();
58 boolean disposed = false;
60 boolean listensDomain;
62 public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {
64 this.listensDomain = listensDomain;
67 private void removeLink(Link<Domain,Range> link) {
68 if(link.domainModified)
69 modifiedDomainLinks.remove(link);
70 if(link.rangeModified)
71 modifiedRangeLinks.remove(link);
75 private void createDomain(WriteGraph g, Link<Domain,Range> link) throws MappingException {
76 LOGGER.info(" createDomain for " + link.rangeElement);
77 ILinkType<Domain,Range> type = schema.linkTypeOfRangeElement(link.rangeElement);
78 Domain domainElement = type.createDomainElement(g, link.rangeElement);
80 link.domainElement = domainElement;
81 domain.put(domainElement, link);
82 type.createDomain(g, new RangeToDomain(g), domainElement, link.rangeElement);
84 // TODO Should we do this only if the mapping is listening?
88 private void createRange(ReadGraph g, Link<Domain,Range> link) throws MappingException {
89 ILinkType<Domain,Range> type = schema.linkTypeOfDomainElement(g, link.domainElement);
90 Range rangeElement = type.createRangeElement(g, link.domainElement);
93 link.rangeElement = rangeElement;
94 range.put(rangeElement, link);
95 type.createRange(g, new DomainToRange(g), link.domainElement, rangeElement);
98 Set<Domain> domainSet = new AbstractSet<Domain>() {
100 public boolean add(Domain e) {
101 if(domain.containsKey(e))
103 Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);
105 modifiedDomainLinks.add(link);
109 public boolean contains(Object o) {
110 return domain.contains(o);
113 public boolean remove(Object o) {
114 Link<Domain,Range> link = domain.remove(o);
118 if(link.rangeElement != null)
119 range.remove(link.rangeElement);
124 public Iterator<Domain> iterator() {
125 // FIXME does not implement Iterator.remove correctly
126 return domain.keySet().iterator();
131 return domain.size();
136 Set<Range> rangeSet = new AbstractSet<Range>() {
138 public boolean add(Range e) {
139 if(range.containsKey(e))
141 Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);
143 modifiedRangeLinks.add(link);
147 public boolean contains(Object o) {
148 return range.contains(o);
151 public boolean remove(Object o) {
152 Link<Domain,Range> link = range.remove(o);
156 if(link.domainElement != null)
157 domain.remove(link.domainElement);
162 public Iterator<Range> iterator() {
163 // FIXME does not implement Iterator.remove correctly
164 return range.keySet().iterator();
174 class DomainToRange implements IForwardMapping<Domain, Range> {
178 public DomainToRange(ReadGraph g) {
183 public Range get(Domain element) {
184 Link<Domain,Range> link = domain.get(element);
186 return link.rangeElement;
192 public Range map(ReadGraph graph, Domain element)
193 throws MappingException {
194 Link<Domain,Range> link = domain.get(element);
196 link = new Link<Domain,Range>(null, element, null);
197 link.domainModified = true;
198 modifiedDomainLinks.add(link);
199 domain.put(element, link);
200 createRange(g, link);
202 else if(link.type == null)
203 createRange(g, link);
204 return link.rangeElement;
208 public Set<Domain> getDomain() {
209 return domain.keySet();
214 class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
218 public RangeToDomain(WriteGraph g) {
223 public Domain inverseGet(Range element) {
225 Link<Domain,Range> link = range.get(element);
227 return link.domainElement;
232 public Domain inverseMap(WriteGraph graph, Range element)
233 throws MappingException {
234 Link<Domain,Range> link = range.get(element);
236 link = new Link<Domain,Range>(null, null, element);
237 link.rangeModified = true;
238 modifiedRangeLinks.add(link);
239 range.put(element, link);
240 createDomain(g, link);
242 else if(link.type == null)
243 createDomain(g, link);
244 return link.domainElement;
249 public Set<Range> getRange() {
250 return range.keySet();
255 public Set<Domain> getDomain() {
260 public Set<Range> getRange() {
266 public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {
267 LOGGER.info("Mapping.updateDomain");
268 RangeToDomain map = new RangeToDomain(g);
269 ArrayList<Domain> updated = new ArrayList<Domain>();
270 while(!modifiedRangeLinks.isEmpty()) {
271 LOGGER.info(" modifiedRangeLinks.size() = " + modifiedRangeLinks.size());
273 Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);
274 link.rangeModified = false;
275 /*if(link.domainModified) {
276 link.domainModified = false;
277 modifiedDomainLinks.remove(link);
280 if(link.type == null) {
281 createDomain(g, link);
284 if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
285 updated.add(link.domainElement);
288 updateRange(g); //FIXME: without this listening would stop.
293 public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
294 LOGGER.info("Mapping.updateRange");
295 DomainToRange map = new DomainToRange(g);
296 ArrayList<Range> updated = new ArrayList<Range>();
297 while(!modifiedDomainLinks.isEmpty()) {
298 LOGGER.info(" modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
300 Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
301 link.domainModified = false;
302 /*if(link.rangeModified) {
303 link.rangeModified = false;
304 modifiedRangeLinks.remove(link);
307 if(link.type == null) {
308 createRange(g, link);
312 RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
314 g.syncRequest(request, request);
315 } catch (DatabaseException e) {
316 throw new MappingException(e);
318 // TODO check if really modified
319 updated.add(link.rangeElement);
322 if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
323 updated.add(link.rangeElement);
329 public Range get(Domain domainElement) {
330 Link<Domain,Range> link = domain.get(domainElement);
333 return link.rangeElement;
337 public Domain inverseGet(Range rangeElement) {
338 Link<Domain,Range> link = range.get(rangeElement);
341 return link.domainElement;
345 public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
346 getRange().add(rangeElement);
348 return inverseGet(rangeElement);
352 public Range map(ReadGraph g, Domain domainElement) throws MappingException {
353 getDomain().add(domainElement);
355 return get(domainElement);
358 void domainModified(Link<Domain,Range> link) {
359 if(!link.domainModified) {
360 synchronized(modifiedDomainLinks) {
361 LOGGER.info(" domainModified for " + link.rangeElement);
362 link.domainModified = true;
363 modifiedDomainLinks.add(link);
364 if(modifiedDomainLinks.size() == 1) {
365 for(IMappingListener listener : listeners)
366 listener.domainModified();
373 public void domainModified(Domain domainElement) {
374 Link<Domain,Range> link = domain.get(domainElement);
376 domainModified(link);
379 void rangeModified(Link<Domain,Range> link) {
380 if(!link.rangeModified) {
381 synchronized(modifiedRangeLinks) {
382 link.rangeModified = true;
383 modifiedRangeLinks.add(link);
384 if(modifiedRangeLinks.size() == 1) {
385 for(IMappingListener listener : listeners)
386 listener.rangeModified();
393 public void rangeModified(Range rangeElement) {
394 Link<Domain,Range> link = range.get(rangeElement);
400 public boolean isDomainModified() {
401 return !modifiedDomainLinks.isEmpty();
405 public boolean isRangeModified() {
406 return !modifiedRangeLinks.isEmpty();
410 public Collection<Domain> getDomainModified() {
411 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
412 for (Link<Domain, Range> link : modifiedDomainLinks)
413 list.add(link.domainElement);
419 public Collection<Range> getRangeModified() {
420 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
421 for (Link<Domain, Range> link : modifiedRangeLinks)
422 list.add(link.rangeElement);
427 public void addMappingListener(IMappingListener listener) {
428 listeners.add(listener);
432 public void removeMappingListener(IMappingListener listener) {
433 listeners.remove(listener);
437 public Collection<Domain> getConflictingDomainElements() {
438 ArrayList<Domain> result = new ArrayList<Domain>();
439 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
440 for(Link<Domain,Range> link : modifiedDomainLinks)
441 if(link.rangeModified)
442 result.add(link.domainElement);
445 for(Link<Domain,Range> link : modifiedRangeLinks)
446 if(link.domainModified)
447 result.add(link.domainElement);
453 public Collection<Range> getConflictingRangeElements() {
454 ArrayList<Range> result = new ArrayList<Range>();
455 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
456 for(Link<Domain,Range> link : modifiedDomainLinks)
457 if(link.rangeModified)
458 result.add(link.rangeElement);
461 for(Link<Domain,Range> link : modifiedRangeLinks)
462 if(link.domainModified)
463 result.add(link.rangeElement);
469 public void dispose() {
473 public boolean isDisposed() {