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.trace(" 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.trace("Mapping.updateDomain");
268 RangeToDomain map = new RangeToDomain(g);
269 ArrayList<Domain> updated = new ArrayList<Domain>();
270 while(!modifiedRangeLinks.isEmpty()) {
271 LOGGER.trace(" 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);
289 updateRange(g); //FIXME: without this listening would stop.
294 public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
295 LOGGER.trace("Mapping.updateRange");
296 DomainToRange map = new DomainToRange(g);
297 ArrayList<Range> updated = new ArrayList<Range>();
298 while(!modifiedDomainLinks.isEmpty()) {
299 LOGGER.trace(" modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
301 Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
302 link.domainModified = false;
303 /*if(link.rangeModified) {
304 link.rangeModified = false;
305 modifiedRangeLinks.remove(link);
308 if(link.type == null) {
309 createRange(g, link);
313 RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
315 g.syncRequest(request, request);
316 } catch (DatabaseException e) {
317 throw new MappingException(e);
319 // TODO check if really modified
320 updated.add(link.rangeElement);
323 if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
324 updated.add(link.rangeElement);
330 public Range get(Domain domainElement) {
331 Link<Domain,Range> link = domain.get(domainElement);
334 return link.rangeElement;
338 public Domain inverseGet(Range rangeElement) {
339 Link<Domain,Range> link = range.get(rangeElement);
342 return link.domainElement;
346 public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
347 getRange().add(rangeElement);
349 return inverseGet(rangeElement);
353 public Range map(ReadGraph g, Domain domainElement) throws MappingException {
354 getDomain().add(domainElement);
356 return get(domainElement);
359 void domainModified(Link<Domain,Range> link) {
360 if(!link.domainModified) {
361 synchronized(modifiedDomainLinks) {
362 LOGGER.trace(" domainModified for " + link.rangeElement);
363 link.domainModified = true;
364 modifiedDomainLinks.add(link);
365 if(modifiedDomainLinks.size() == 1) {
366 for(IMappingListener listener : listeners)
367 listener.domainModified();
374 public void domainModified(Domain domainElement) {
375 Link<Domain,Range> link = domain.get(domainElement);
377 domainModified(link);
380 void rangeModified(Link<Domain,Range> link) {
381 if(!link.rangeModified) {
382 synchronized(modifiedRangeLinks) {
383 link.rangeModified = true;
384 modifiedRangeLinks.add(link);
385 if(modifiedRangeLinks.size() == 1) {
386 for(IMappingListener listener : listeners)
387 listener.rangeModified();
394 public void rangeModified(Range rangeElement) {
395 Link<Domain,Range> link = range.get(rangeElement);
401 public boolean isDomainModified() {
402 return !modifiedDomainLinks.isEmpty();
406 public boolean isRangeModified() {
407 return !modifiedRangeLinks.isEmpty();
411 public Collection<Domain> getDomainModified() {
412 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
413 for (Link<Domain, Range> link : modifiedDomainLinks)
414 list.add(link.domainElement);
420 public Collection<Range> getRangeModified() {
421 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
422 for (Link<Domain, Range> link : modifiedRangeLinks)
423 list.add(link.rangeElement);
428 public void addMappingListener(IMappingListener listener) {
429 listeners.add(listener);
433 public void removeMappingListener(IMappingListener listener) {
434 listeners.remove(listener);
438 public Collection<Domain> getConflictingDomainElements() {
439 ArrayList<Domain> result = new ArrayList<Domain>();
440 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
441 for(Link<Domain,Range> link : modifiedDomainLinks)
442 if(link.rangeModified)
443 result.add(link.domainElement);
446 for(Link<Domain,Range> link : modifiedRangeLinks)
447 if(link.domainModified)
448 result.add(link.domainElement);
454 public Collection<Range> getConflictingRangeElements() {
455 ArrayList<Range> result = new ArrayList<Range>();
456 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
457 for(Link<Domain,Range> link : modifiedDomainLinks)
458 if(link.rangeModified)
459 result.add(link.rangeElement);
462 for(Link<Domain,Range> link : modifiedRangeLinks)
463 if(link.domainModified)
464 result.add(link.rangeElement);
470 public void dispose() {
474 public boolean isDisposed() {