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