]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java
9f048f7752b1a900a1730eae5242d53519d21a8b
[simantics/platform.git] / bundles / org.simantics.objmap2 / src / org / simantics / objmap / graph / impl / Mapping.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2013 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.objmap.graph.impl;
13
14
15 import gnu.trove.map.hash.THashMap;
16
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;
22 import java.util.Set;
23
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;
29
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;
37
38 /**
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ö
43  */
44 public class Mapping<Domain, Range> implements IMapping<Domain, Range> {
45
46         static final Logger LOGGER = LoggerFactory.getLogger(Mapping.class);
47         
48         
49         IMappingSchema<Domain, Range> schema;
50         
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>();
54
55         ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();
56         ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();
57
58         boolean disposed = false;
59         
60         boolean listensDomain; 
61         
62         public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {
63                 this.schema = schema;
64                 this.listensDomain = listensDomain;
65         }
66         
67         private void removeLink(Link<Domain,Range> link) {
68                 if(link.domainModified)
69                         modifiedDomainLinks.remove(link);
70                 if(link.rangeModified)
71                         modifiedRangeLinks.remove(link);
72                 link.removed = true;
73         }
74         
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);
79                 link.type = type;
80                 link.domainElement = domainElement;
81                 domain.put(domainElement, link);
82                 type.createDomain(g, new RangeToDomain(g), domainElement, link.rangeElement);
83                 
84         // TODO Should we do this only if the mapping is listening?
85         domainModified(link);
86         }
87         
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);
91                 
92                 link.type = type;
93                 link.rangeElement = rangeElement;
94                 range.put(rangeElement, link);
95                 type.createRange(g, new DomainToRange(g), link.domainElement, rangeElement);
96         }
97         
98         Set<Domain> domainSet = new AbstractSet<Domain>() {
99
100                 public boolean add(Domain e) {
101                         if(domain.containsKey(e))
102                                 return false;
103                         Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);
104                         domain.put(e, link);
105                         modifiedDomainLinks.add(link);
106                         return true;
107                 }
108                 
109                 public boolean contains(Object o) {
110                         return domain.contains(o);
111                 }
112                 
113                 public boolean remove(Object o) {
114                         Link<Domain,Range> link = domain.remove(o);                     
115                         if(link == null)
116                                 return false;
117                         removeLink(link);
118                         if(link.rangeElement != null)
119                                 range.remove(link.rangeElement);
120                         return true;    
121                 }
122                 
123                 @Override
124                 public Iterator<Domain> iterator() {
125                     // FIXME does not implement Iterator.remove correctly
126                         return domain.keySet().iterator();
127                 }
128
129                 @Override
130                 public int size() {
131                         return domain.size();
132                 }
133                 
134         };
135         
136         Set<Range> rangeSet = new AbstractSet<Range>() {
137
138                 public boolean add(Range e) {
139                         if(range.containsKey(e))
140                                 return false;
141                         Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);
142                         range.put(e, link);
143                         modifiedRangeLinks.add(link);
144                         return true;
145                 }
146                 
147                 public boolean contains(Object o) {
148                         return range.contains(o);
149                 }
150                 
151                 public boolean remove(Object o) {
152                         Link<Domain,Range> link = range.remove(o);                      
153                         if(link == null)
154                                 return false;
155                         removeLink(link);
156                         if(link.domainElement != null)
157                                 domain.remove(link.domainElement);
158                         return true;
159                 }
160                 
161                 @Override
162                 public Iterator<Range> iterator() {
163                     // FIXME does not implement Iterator.remove correctly
164                         return range.keySet().iterator();
165                 }
166
167                 @Override
168                 public int size() {
169                         return range.size();
170                 }
171                 
172         };
173         
174         class DomainToRange implements IForwardMapping<Domain, Range> {
175
176                 ReadGraph g;
177                 
178                 public DomainToRange(ReadGraph g) {
179                         this.g = g;
180                 }
181
182                 @Override
183                 public Range get(Domain element)  {
184                         Link<Domain,Range> link = domain.get(element);
185                         if (link != null)
186                                 return link.rangeElement;
187                         return null;
188                         
189                 }
190                 
191                 @Override
192                 public Range map(ReadGraph graph, Domain element)
193                                 throws MappingException {
194                         Link<Domain,Range> link = domain.get(element);
195                         if(link == null) {
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);       
201                         }
202                         else if(link.type == null) 
203                                 createRange(g, link);
204             return link.rangeElement;
205                 }
206                 
207                 @Override
208                 public Set<Domain> getDomain() {
209                         return domain.keySet();
210                 }
211                 
212         };
213         
214         class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {
215
216                 WriteGraph g;
217                 
218                 public RangeToDomain(WriteGraph g) {
219                         super(g);
220                         this.g = g;
221                 }
222                 @Override
223                 public Domain inverseGet(Range element) {
224                         
225                         Link<Domain,Range> link = range.get(element);
226                         if(link != null)
227                                 return link.domainElement;
228                         return null;
229                 }
230                 
231                 @Override
232                 public Domain inverseMap(WriteGraph graph, Range element)
233                                 throws MappingException {
234                         Link<Domain,Range> link = range.get(element);
235                         if(link == null) {
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);                              
241                         }
242                         else if(link.type == null)
243                                 createDomain(g, link);
244                         return link.domainElement;
245                 }
246                 
247                 
248                 @Override
249                 public Set<Range> getRange() {
250                         return range.keySet();
251                 }
252         };
253         
254         @Override
255         public Set<Domain> getDomain() {
256                 return domainSet;
257         }
258         
259         @Override
260         public Set<Range> getRange() {
261                 return rangeSet;
262         }
263         
264         
265         @Override
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());
272                     
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);
278                         }*/
279                         
280                         if(link.type == null) {
281                                 createDomain(g, link);
282                         }
283                         
284                         if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))
285                                 updated.add(link.domainElement);
286                 }       
287                 if (listensDomain)
288                         updateRange(g); //FIXME: without this listening would stop. 
289                 return updated;
290         }
291         
292         @Override
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());
299                     
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);
305                         }*/
306                         
307                         if(link.type == null) {
308                                 createRange(g, link);
309                         }
310                         
311                         if(listensDomain) {
312                             RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);
313                             try {
314                     g.syncRequest(request, request);
315                 } catch (DatabaseException e) {
316                     throw new MappingException(e);
317                 }
318                             // TODO check if really modified
319                             updated.add(link.rangeElement);
320                         }
321                         else
322                             if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))
323                                 updated.add(link.rangeElement);
324                 }       
325                 return updated;
326         }
327
328         @Override
329         public Range get(Domain domainElement) {
330                 Link<Domain,Range> link = domain.get(domainElement);
331                 if(link == null)
332                         return null;
333                 return link.rangeElement;
334         }
335
336         @Override
337         public Domain inverseGet(Range rangeElement) {
338                 Link<Domain,Range> link = range.get(rangeElement);
339                 if(link == null)
340                         return null;
341                 return link.domainElement;
342         }
343
344         @Override
345         public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {
346                 getRange().add(rangeElement);
347                 updateDomain(g);
348                 return inverseGet(rangeElement);
349         }
350
351         @Override
352         public Range map(ReadGraph g, Domain domainElement) throws MappingException {
353                 getDomain().add(domainElement);
354                 updateRange(g);
355                 return get(domainElement);
356         }
357
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();
367                 }
368                 }
369         }
370         }
371         
372         @Override
373         public void domainModified(Domain domainElement) {
374                 Link<Domain,Range> link = domain.get(domainElement);
375                 if(link != null)
376                     domainModified(link);
377         }
378
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();
387                 }
388                 }
389         }
390         }
391         
392         @Override
393         public void rangeModified(Range rangeElement) {
394                 Link<Domain,Range> link = range.get(rangeElement);
395                 if(link != null)
396                     rangeModified(link);
397         }
398
399         @Override
400         public boolean isDomainModified() {
401                 return !modifiedDomainLinks.isEmpty();
402         }
403
404         @Override
405         public boolean isRangeModified() {
406                 return !modifiedRangeLinks.isEmpty();
407         }
408         
409         @Override
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);
414                 return list;
415                                 
416         }
417         
418         @Override
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);
423                 return list;
424         }
425
426         @Override
427         public void addMappingListener(IMappingListener listener) {
428                 listeners.add(listener);
429         }
430
431         @Override
432         public void removeMappingListener(IMappingListener listener) {
433                 listeners.remove(listener);             
434         }
435
436         @Override
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);
443                 }
444                 else {
445                         for(Link<Domain,Range> link : modifiedRangeLinks)
446                                 if(link.domainModified)
447                                         result.add(link.domainElement);
448                 }
449                 return result;
450         }
451
452         @Override
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);
459                 }
460                 else {
461                         for(Link<Domain,Range> link : modifiedRangeLinks)
462                                 if(link.domainModified)
463                                         result.add(link.rangeElement);
464                 }
465                 return result;
466         }
467
468     @Override
469     public void dispose() {
470         disposed = true;
471     }
472     
473     public boolean isDisposed() {
474         return disposed;
475     }
476         
477 }