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(), 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(), 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> {
176 public DomainToRange() {
180 public Range get(Domain element) {
181 Link<Domain,Range> link = domain.get(element);
183 return link.rangeElement;
189 public Range map(ReadGraph graph, Domain element)
190 throws MappingException {
191 Link<Domain,Range> link = domain.get(element);
193 link = new Link<Domain,Range>(null, element, null);
194 link.domainModified = true;
195 modifiedDomainLinks.add(link);
196 domain.put(element, link);
197 createRange(graph, link);
199 else if(link.type == null)
200 createRange(graph, link);
201 return link.rangeElement;
205 public Set<Domain> getDomain() {
206 return domain.keySet();
211 class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
213 public RangeToDomain() {
218 public Domain inverseGet(Range element) {
220 Link<Domain,Range> link = range.get(element);
222 return link.domainElement;
227 public Domain inverseMap(WriteGraph graph, Range element)
228 throws MappingException {
229 Link<Domain,Range> link = range.get(element);
231 link = new Link<Domain,Range>(null, null, element);
232 link.rangeModified = true;
233 modifiedRangeLinks.add(link);
234 range.put(element, link);
235 createDomain(graph, link);
237 else if(link.type == null)
238 createDomain(graph, link);
239 return link.domainElement;
244 public Set<Range> getRange() {
245 return range.keySet();
250 public Set<Domain> getDomain() {
255 public Set<Range> getRange() {
261 public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {
262 LOGGER.trace("Mapping.updateDomain");
263 RangeToDomain map = new RangeToDomain();
264 ArrayList<Domain> updated = new ArrayList<Domain>();
265 while(!modifiedRangeLinks.isEmpty()) {
266 LOGGER.trace(" modifiedRangeLinks.size() = " + modifiedRangeLinks.size());
268 Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);
269 link.rangeModified = false;
270 /*if(link.domainModified) {
271 link.domainModified = false;
272 modifiedDomainLinks.remove(link);
275 if(link.type == null) {
276 createDomain(g, link);
279 if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
280 updated.add(link.domainElement);
284 updateRange(g); //FIXME: without this listening would stop.
289 public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
290 LOGGER.trace("Mapping.updateRange");
291 DomainToRange map = new DomainToRange();
292 ArrayList<Range> updated = new ArrayList<Range>();
293 while(!modifiedDomainLinks.isEmpty()) {
294 LOGGER.trace(" modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
296 Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
297 link.domainModified = false;
298 /*if(link.rangeModified) {
299 link.rangeModified = false;
300 modifiedRangeLinks.remove(link);
303 if(link.type == null) {
304 createRange(g, link);
308 RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
310 g.syncRequest(request, request);
311 } catch (DatabaseException e) {
312 throw new MappingException(e);
314 // TODO check if really modified
315 updated.add(link.rangeElement);
318 if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
319 updated.add(link.rangeElement);
325 public Range get(Domain domainElement) {
326 Link<Domain,Range> link = domain.get(domainElement);
329 return link.rangeElement;
333 public Domain inverseGet(Range rangeElement) {
334 Link<Domain,Range> link = range.get(rangeElement);
337 return link.domainElement;
341 public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
342 getRange().add(rangeElement);
344 return inverseGet(rangeElement);
348 public Range map(ReadGraph g, Domain domainElement) throws MappingException {
349 getDomain().add(domainElement);
351 return get(domainElement);
354 void domainModified(Link<Domain,Range> link) {
355 if(!link.domainModified) {
356 synchronized(modifiedDomainLinks) {
357 LOGGER.trace(" domainModified for " + link.rangeElement);
358 link.domainModified = true;
359 modifiedDomainLinks.add(link);
360 if(modifiedDomainLinks.size() == 1) {
361 for(IMappingListener listener : listeners)
362 listener.domainModified();
369 public void domainModified(Domain domainElement) {
370 Link<Domain,Range> link = domain.get(domainElement);
372 domainModified(link);
375 void rangeModified(Link<Domain,Range> link) {
376 if(!link.rangeModified) {
377 synchronized(modifiedRangeLinks) {
378 link.rangeModified = true;
379 modifiedRangeLinks.add(link);
380 if(modifiedRangeLinks.size() == 1) {
381 for(IMappingListener listener : listeners)
382 listener.rangeModified();
389 public void rangeModified(Range rangeElement) {
390 Link<Domain,Range> link = range.get(rangeElement);
396 public boolean isDomainModified() {
397 return !modifiedDomainLinks.isEmpty();
401 public boolean isRangeModified() {
402 return !modifiedRangeLinks.isEmpty();
406 public Collection<Domain> getDomainModified() {
407 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
408 for (Link<Domain, Range> link : modifiedDomainLinks)
409 list.add(link.domainElement);
415 public Collection<Range> getRangeModified() {
416 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
417 for (Link<Domain, Range> link : modifiedRangeLinks)
418 list.add(link.rangeElement);
423 public void addMappingListener(IMappingListener listener) {
424 listeners.add(listener);
428 public void removeMappingListener(IMappingListener listener) {
429 listeners.remove(listener);
433 public Collection<Domain> getConflictingDomainElements() {
434 ArrayList<Domain> result = new ArrayList<Domain>();
435 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
436 for(Link<Domain,Range> link : modifiedDomainLinks)
437 if(link.rangeModified)
438 result.add(link.domainElement);
441 for(Link<Domain,Range> link : modifiedRangeLinks)
442 if(link.domainModified)
443 result.add(link.domainElement);
449 public Collection<Range> getConflictingRangeElements() {
450 ArrayList<Range> result = new ArrayList<Range>();
451 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
452 for(Link<Domain,Range> link : modifiedDomainLinks)
453 if(link.rangeModified)
454 result.add(link.rangeElement);
457 for(Link<Domain,Range> link : modifiedRangeLinks)
458 if(link.domainModified)
459 result.add(link.rangeElement);
465 public void dispose() {
469 public boolean isDisposed() {