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 java.util.AbstractSet;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Iterator;
19 import java.util.List;
22 import org.simantics.db.ReadGraph;
23 import org.simantics.db.WriteGraph;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.objmap.backward.IBackwardMapping;
26 import org.simantics.objmap.exceptions.MappingException;
27 import org.simantics.objmap.forward.IForwardMapping;
28 import org.simantics.objmap.graph.IMapping;
29 import org.simantics.objmap.graph.IMappingListener;
30 import org.simantics.objmap.graph.schema.ILinkType;
31 import org.simantics.objmap.graph.schema.IMappingSchema;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 import gnu.trove.map.hash.THashMap;
38 * An implementation of IMapping. The class should not be created
39 * directly but using methods in Mappings.
40 * @see org.simantics.objmap.graph.Mappings
41 * @author Hannu Niemistö
43 public class Mapping<Domain, Range> implements IMapping<Domain, Range> {
45 static final Logger LOGGER = LoggerFactory.getLogger(Mapping.class);
48 IMappingSchema<Domain, Range> schema;
50 THashMap<Domain, Link<Domain,Range>> domain = new THashMap<Domain, Link<Domain,Range>>();
51 THashMap<Range, Link<Domain,Range>> range = new THashMap<Range, Link<Domain,Range>>();
52 ArrayList<IMappingListener> listeners = new ArrayList<IMappingListener>();
54 ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();
55 ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();
57 boolean disposed = false;
59 boolean listensDomain;
61 public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {
63 this.listensDomain = listensDomain;
66 private void removeLink(Link<Domain,Range> link) {
67 if(link.domainModified)
68 modifiedDomainLinks.remove(link);
69 if(link.rangeModified)
70 modifiedRangeLinks.remove(link);
74 private void createDomain(WriteGraph g, Link<Domain,Range> link) throws MappingException {
75 LOGGER.trace(" createDomain for " + link.rangeElement);
76 ILinkType<Domain,Range> type = schema.linkTypeOfRangeElement(link.rangeElement);
77 Domain domainElement = type.createDomainElement(g, link.rangeElement);
79 link.domainElement = domainElement;
80 domain.put(domainElement, link);
81 type.createDomain(g, new RangeToDomain(), domainElement, link.rangeElement);
83 // TODO Should we do this only if the mapping is listening?
87 private void createRange(ReadGraph g, Link<Domain,Range> link) throws MappingException {
88 ILinkType<Domain,Range> type = schema.linkTypeOfDomainElement(g, link.domainElement);
89 Range rangeElement = type.createRangeElement(g, link.domainElement);
92 link.rangeElement = rangeElement;
93 range.put(rangeElement, link);
94 type.createRange(g, new DomainToRange(), link.domainElement, rangeElement);
97 Set<Domain> domainSet = new AbstractSet<Domain>() {
99 public boolean add(Domain e) {
100 if(domain.containsKey(e))
102 Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);
104 modifiedDomainLinks.add(link);
108 public boolean contains(Object o) {
109 return domain.contains(o);
112 public boolean remove(Object o) {
113 Link<Domain,Range> link = domain.remove(o);
117 if(link.rangeElement != null)
118 range.remove(link.rangeElement);
123 public Iterator<Domain> iterator() {
124 // FIXME does not implement Iterator.remove correctly
125 return domain.keySet().iterator();
130 return domain.size();
135 Set<Range> rangeSet = new AbstractSet<Range>() {
137 public boolean add(Range e) {
138 if(range.containsKey(e))
140 Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);
142 modifiedRangeLinks.add(link);
146 public boolean contains(Object o) {
147 return range.contains(o);
150 public boolean remove(Object o) {
151 Link<Domain,Range> link = range.remove(o);
155 if(link.domainElement != null)
156 domain.remove(link.domainElement);
161 public Iterator<Range> iterator() {
162 // FIXME does not implement Iterator.remove correctly
163 return range.keySet().iterator();
173 class DomainToRange implements IForwardMapping<Domain, Range> {
175 public DomainToRange() {
179 public Range get(Domain element) {
180 Link<Domain,Range> link = domain.get(element);
182 return link.rangeElement;
188 public Range map(ReadGraph graph, Domain element)
189 throws MappingException {
190 Link<Domain,Range> link = domain.get(element);
192 link = new Link<Domain,Range>(null, element, null);
193 link.domainModified = true;
194 modifiedDomainLinks.add(link);
195 domain.put(element, link);
196 createRange(graph, link);
198 else if(link.type == null)
199 createRange(graph, link);
200 return link.rangeElement;
204 public Set<Domain> getDomain() {
205 return domain.keySet();
210 class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
212 public RangeToDomain() {
217 public Domain inverseGet(Range element) {
219 Link<Domain,Range> link = range.get(element);
221 return link.domainElement;
226 public Domain inverseMap(WriteGraph graph, Range element)
227 throws MappingException {
228 Link<Domain,Range> link = range.get(element);
230 link = new Link<Domain,Range>(null, null, element);
231 link.rangeModified = true;
232 modifiedRangeLinks.add(link);
233 range.put(element, link);
234 createDomain(graph, link);
236 else if(link.type == null)
237 createDomain(graph, link);
238 return link.domainElement;
243 public Set<Range> getRange() {
244 return range.keySet();
249 public Set<Domain> getDomain() {
254 public Set<Range> getRange() {
260 public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {
261 LOGGER.trace("Mapping.updateDomain");
262 RangeToDomain map = new RangeToDomain();
263 ArrayList<Domain> updated = new ArrayList<Domain>();
264 while(!modifiedRangeLinks.isEmpty()) {
265 LOGGER.trace(" modifiedRangeLinks.size() = " + modifiedRangeLinks.size());
267 Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);
268 link.rangeModified = false;
269 /*if(link.domainModified) {
270 link.domainModified = false;
271 modifiedDomainLinks.remove(link);
274 if(link.type == null) {
275 createDomain(g, link);
278 if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
279 updated.add(link.domainElement);
283 updateRange(g); //FIXME: without this listening would stop.
288 public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
289 LOGGER.trace("Mapping.updateRange");
290 DomainToRange map = new DomainToRange();
291 ArrayList<Range> updated = new ArrayList<Range>();
292 while(!modifiedDomainLinks.isEmpty()) {
293 LOGGER.trace(" modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
295 Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
296 link.domainModified = false;
297 /*if(link.rangeModified) {
298 link.rangeModified = false;
299 modifiedRangeLinks.remove(link);
302 if(link.type == null) {
303 createRange(g, link);
307 RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
310 changes = g.syncRequest(request, request) > 0;
311 } catch (DatabaseException e) {
312 throw new MappingException(e);
316 updated.add(link.rangeElement);
319 if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
320 updated.add(link.rangeElement);
326 public Range get(Domain domainElement) {
327 Link<Domain,Range> link = domain.get(domainElement);
330 return link.rangeElement;
334 public Domain inverseGet(Range rangeElement) {
335 Link<Domain,Range> link = range.get(rangeElement);
338 return link.domainElement;
342 public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
343 getRange().add(rangeElement);
345 return inverseGet(rangeElement);
349 public Range map(ReadGraph g, Domain domainElement) throws MappingException {
350 getDomain().add(domainElement);
352 return get(domainElement);
355 void domainModified(Link<Domain,Range> link) {
356 if(!link.domainModified) {
357 synchronized(modifiedDomainLinks) {
358 LOGGER.trace(" domainModified for " + link.rangeElement);
359 link.domainModified = true;
360 modifiedDomainLinks.add(link);
361 if(modifiedDomainLinks.size() == 1) {
362 for(IMappingListener listener : listeners)
363 listener.domainModified();
370 public void domainModified(Domain domainElement) {
371 Link<Domain,Range> link = domain.get(domainElement);
373 domainModified(link);
376 void rangeModified(Link<Domain,Range> link) {
377 if(!link.rangeModified) {
378 synchronized(modifiedRangeLinks) {
379 link.rangeModified = true;
380 modifiedRangeLinks.add(link);
381 if(modifiedRangeLinks.size() == 1) {
382 for(IMappingListener listener : listeners)
383 listener.rangeModified();
390 public void rangeModified(Range rangeElement) {
391 Link<Domain,Range> link = range.get(rangeElement);
397 public boolean isDomainModified() {
398 return !modifiedDomainLinks.isEmpty();
402 public boolean isRangeModified() {
403 return !modifiedRangeLinks.isEmpty();
407 public Collection<Domain> getDomainModified() {
408 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
409 for (Link<Domain, Range> link : modifiedDomainLinks)
410 list.add(link.domainElement);
416 public Collection<Range> getRangeModified() {
417 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
418 for (Link<Domain, Range> link : modifiedRangeLinks)
419 list.add(link.rangeElement);
424 public void addMappingListener(IMappingListener listener) {
425 listeners.add(listener);
429 public void removeMappingListener(IMappingListener listener) {
430 listeners.remove(listener);
434 public Collection<Domain> getConflictingDomainElements() {
435 ArrayList<Domain> result = new ArrayList<Domain>();
436 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
437 for(Link<Domain,Range> link : modifiedDomainLinks)
438 if(link.rangeModified)
439 result.add(link.domainElement);
442 for(Link<Domain,Range> link : modifiedRangeLinks)
443 if(link.domainModified)
444 result.add(link.domainElement);
450 public Collection<Range> getConflictingRangeElements() {
451 ArrayList<Range> result = new ArrayList<Range>();
452 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
453 for(Link<Domain,Range> link : modifiedDomainLinks)
454 if(link.rangeModified)
455 result.add(link.rangeElement);
458 for(Link<Domain,Range> link : modifiedRangeLinks)
459 if(link.domainModified)
460 result.add(link.rangeElement);
466 public void dispose() {
470 public boolean isDisposed() {