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.apache.log4j.Logger;
25 import org.simantics.db.ReadGraph;
26 import org.simantics.db.WriteGraph;
27 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.objmap.backward.IBackwardMapping;
30 import org.simantics.objmap.exceptions.MappingException;
31 import org.simantics.objmap.forward.IForwardMapping;
32 import org.simantics.objmap.graph.IMapping;
33 import org.simantics.objmap.graph.IMappingListener;
34 import org.simantics.objmap.graph.schema.ILinkType;
35 import org.simantics.objmap.graph.schema.IMappingSchema;
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 Logger LOGGER = Logger.getLogger("org.simantics.objmap");
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.info(" 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(g), 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(g), 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> {
177 public DomainToRange(ReadGraph g) {
182 public Range get(Domain element) {
183 Link<Domain,Range> link = domain.get(element);
185 return link.rangeElement;
191 public Range map(ReadGraph graph, Domain element)
192 throws MappingException {
193 Link<Domain,Range> link = domain.get(element);
195 link = new Link<Domain,Range>(null, element, null);
196 link.domainModified = true;
197 modifiedDomainLinks.add(link);
198 domain.put(element, link);
199 createRange(g, link);
201 else if(link.type == null)
202 createRange(g, link);
203 return link.rangeElement;
207 public Set<Domain> getDomain() {
208 return domain.keySet();
213 class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
217 public RangeToDomain(WriteGraph g) {
222 public Domain inverseGet(Range element) {
224 Link<Domain,Range> link = range.get(element);
226 return link.domainElement;
231 public Domain inverseMap(WriteGraph graph, Range element)
232 throws MappingException {
233 Link<Domain,Range> link = range.get(element);
235 link = new Link<Domain,Range>(null, null, element);
236 link.rangeModified = true;
237 modifiedRangeLinks.add(link);
238 range.put(element, link);
239 createDomain(g, link);
241 else if(link.type == null)
242 createDomain(g, link);
243 return link.domainElement;
248 public Set<Range> getRange() {
249 return range.keySet();
254 public Set<Domain> getDomain() {
259 public Set<Range> getRange() {
265 public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {
266 LOGGER.info("Mapping.updateDomain");
267 RangeToDomain map = new RangeToDomain(g);
268 ArrayList<Domain> updated = new ArrayList<Domain>();
269 while(!modifiedRangeLinks.isEmpty()) {
270 LOGGER.info(" modifiedRangeLinks.size() = " + modifiedRangeLinks.size());
272 Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);
273 link.rangeModified = false;
274 /*if(link.domainModified) {
275 link.domainModified = false;
276 modifiedDomainLinks.remove(link);
279 if(link.type == null) {
280 createDomain(g, link);
283 if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
284 updated.add(link.domainElement);
287 updateRange(g); //FIXME: without this listening would stop.
292 public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {
293 LOGGER.info("Mapping.updateRange");
294 DomainToRange map = new DomainToRange(g);
295 ArrayList<Range> updated = new ArrayList<Range>();
296 while(!modifiedDomainLinks.isEmpty()) {
297 LOGGER.info(" modifiedDomainLinks.size() = " + modifiedDomainLinks.size());
299 Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);
300 link.domainModified = false;
301 /*if(link.rangeModified) {
302 link.rangeModified = false;
303 modifiedRangeLinks.remove(link);
306 if(link.type == null) {
307 createRange(g, link);
311 RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
313 g.syncRequest(request, request);
314 } catch (DatabaseException e) {
315 throw new MappingException(e);
317 // TODO check if really modified
318 updated.add(link.rangeElement);
321 if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
322 updated.add(link.rangeElement);
328 public Range get(Domain domainElement) {
329 Link<Domain,Range> link = domain.get(domainElement);
332 return link.rangeElement;
336 public Domain inverseGet(Range rangeElement) {
337 Link<Domain,Range> link = range.get(rangeElement);
340 return link.domainElement;
344 public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
345 getRange().add(rangeElement);
347 return inverseGet(rangeElement);
351 public Range map(ReadGraph g, Domain domainElement) throws MappingException {
352 getDomain().add(domainElement);
354 return get(domainElement);
357 void domainModified(Link<Domain,Range> link) {
358 if(!link.domainModified) {
359 synchronized(modifiedDomainLinks) {
360 LOGGER.info(" domainModified for " + link.rangeElement);
361 link.domainModified = true;
362 modifiedDomainLinks.add(link);
363 if(modifiedDomainLinks.size() == 1) {
364 for(IMappingListener listener : listeners)
365 listener.domainModified();
372 public void domainModified(Domain domainElement) {
373 Link<Domain,Range> link = domain.get(domainElement);
375 domainModified(link);
378 void rangeModified(Link<Domain,Range> link) {
379 if(!link.rangeModified) {
380 synchronized(modifiedRangeLinks) {
381 link.rangeModified = true;
382 modifiedRangeLinks.add(link);
383 if(modifiedRangeLinks.size() == 1) {
384 for(IMappingListener listener : listeners)
385 listener.rangeModified();
392 public void rangeModified(Range rangeElement) {
393 Link<Domain,Range> link = range.get(rangeElement);
399 public boolean isDomainModified() {
400 return !modifiedDomainLinks.isEmpty();
404 public boolean isRangeModified() {
405 return !modifiedRangeLinks.isEmpty();
409 public Collection<Domain> getDomainModified() {
410 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());
411 for (Link<Domain, Range> link : modifiedDomainLinks)
412 list.add(link.domainElement);
418 public Collection<Range> getRangeModified() {
419 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());
420 for (Link<Domain, Range> link : modifiedRangeLinks)
421 list.add(link.rangeElement);
426 public void addMappingListener(IMappingListener listener) {
427 listeners.add(listener);
431 public void removeMappingListener(IMappingListener listener) {
432 listeners.remove(listener);
436 public Collection<Domain> getConflictingDomainElements() {
437 ArrayList<Domain> result = new ArrayList<Domain>();
438 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
439 for(Link<Domain,Range> link : modifiedDomainLinks)
440 if(link.rangeModified)
441 result.add(link.domainElement);
444 for(Link<Domain,Range> link : modifiedRangeLinks)
445 if(link.domainModified)
446 result.add(link.domainElement);
452 public Collection<Range> getConflictingRangeElements() {
453 ArrayList<Range> result = new ArrayList<Range>();
454 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {
455 for(Link<Domain,Range> link : modifiedDomainLinks)
456 if(link.rangeModified)
457 result.add(link.rangeElement);
460 for(Link<Domain,Range> link : modifiedRangeLinks)
461 if(link.domainModified)
462 result.add(link.rangeElement);
468 public void dispose() {
472 public boolean isDisposed() {