]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/impl/Mapping.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.objmap2 / src / org / simantics / objmap / graph / impl / Mapping.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2013 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.objmap.graph.impl;\r
13 \r
14 \r
15 import gnu.trove.map.hash.THashMap;\r
16 \r
17 import java.util.AbstractSet;\r
18 import java.util.ArrayList;\r
19 import java.util.Collection;\r
20 import java.util.Iterator;\r
21 import java.util.List;\r
22 import java.util.Set;\r
23 \r
24 import org.apache.log4j.Logger;\r
25 import org.simantics.db.ReadGraph;\r
26 import org.simantics.db.WriteGraph;\r
27 import org.simantics.db.exception.DatabaseException;\r
28 \r
29 import org.simantics.objmap.backward.IBackwardMapping;\r
30 import org.simantics.objmap.exceptions.MappingException;\r
31 import org.simantics.objmap.forward.IForwardMapping;\r
32 import org.simantics.objmap.graph.IMapping;\r
33 import org.simantics.objmap.graph.IMappingListener;\r
34 import org.simantics.objmap.graph.schema.ILinkType;\r
35 import org.simantics.objmap.graph.schema.IMappingSchema;\r
36 \r
37 /**\r
38  * An implementation of IMapping. The class should not be created\r
39  * directly but using methods in Mappings.\r
40  * @see org.simantics.objmap.graph.Mappings\r
41  * @author Hannu Niemistö\r
42  */\r
43 public class Mapping<Domain, Range> implements IMapping<Domain, Range> {\r
44 \r
45         static Logger LOGGER = Logger.getLogger("org.simantics.objmap");\r
46         \r
47         \r
48         IMappingSchema<Domain, Range> schema;\r
49         \r
50         THashMap<Domain, Link<Domain,Range>> domain = new THashMap<Domain, Link<Domain,Range>>();\r
51         THashMap<Range, Link<Domain,Range>> range = new THashMap<Range, Link<Domain,Range>>();\r
52         ArrayList<IMappingListener> listeners = new ArrayList<IMappingListener>();\r
53 \r
54         ArrayList<Link<Domain,Range>> modifiedDomainLinks = new ArrayList<Link<Domain,Range>>();\r
55         ArrayList<Link<Domain,Range>> modifiedRangeLinks = new ArrayList<Link<Domain,Range>>();\r
56 \r
57         boolean disposed = false;\r
58         \r
59         boolean listensDomain; \r
60         \r
61         public Mapping(IMappingSchema<Domain, Range> schema, boolean listensDomain) {\r
62                 this.schema = schema;\r
63                 this.listensDomain = listensDomain;\r
64         }\r
65         \r
66         private void removeLink(Link<Domain,Range> link) {\r
67                 if(link.domainModified)\r
68                         modifiedDomainLinks.remove(link);\r
69                 if(link.rangeModified)\r
70                         modifiedRangeLinks.remove(link);\r
71                 link.removed = true;\r
72         }\r
73         \r
74         private void createDomain(WriteGraph g, Link<Domain,Range> link) throws MappingException {\r
75             LOGGER.info("        createDomain for " + link.rangeElement);\r
76                 ILinkType<Domain,Range> type = schema.linkTypeOfRangeElement(link.rangeElement);\r
77                 Domain domainElement = type.createDomainElement(g, link.rangeElement);\r
78                 link.type = type;\r
79                 link.domainElement = domainElement;\r
80                 domain.put(domainElement, link);\r
81                 type.createDomain(g, new RangeToDomain(g), domainElement, link.rangeElement);\r
82                 \r
83         // TODO Should we do this only if the mapping is listening?\r
84         domainModified(link);\r
85         }\r
86         \r
87         private void createRange(ReadGraph g, Link<Domain,Range> link) throws MappingException {\r
88                 ILinkType<Domain,Range> type = schema.linkTypeOfDomainElement(g, link.domainElement);           \r
89                 Range rangeElement = type.createRangeElement(g, link.domainElement);\r
90                 \r
91                 link.type = type;\r
92                 link.rangeElement = rangeElement;\r
93                 range.put(rangeElement, link);\r
94                 type.createRange(g, new DomainToRange(g), link.domainElement, rangeElement);\r
95         }\r
96         \r
97         Set<Domain> domainSet = new AbstractSet<Domain>() {\r
98 \r
99                 public boolean add(Domain e) {\r
100                         if(domain.containsKey(e))\r
101                                 return false;\r
102                         Link<Domain,Range> link = new Link<Domain,Range>(null, e, null);\r
103                         domain.put(e, link);\r
104                         modifiedDomainLinks.add(link);\r
105                         return true;\r
106                 }\r
107                 \r
108                 public boolean contains(Object o) {\r
109                         return domain.contains(o);\r
110                 }\r
111                 \r
112                 public boolean remove(Object o) {\r
113                         Link<Domain,Range> link = domain.remove(o);                     \r
114                         if(link == null)\r
115                                 return false;\r
116                         removeLink(link);\r
117                         if(link.rangeElement != null)\r
118                                 range.remove(link.rangeElement);\r
119                         return true;    \r
120                 }\r
121                 \r
122                 @Override\r
123                 public Iterator<Domain> iterator() {\r
124                     // FIXME does not implement Iterator.remove correctly\r
125                         return domain.keySet().iterator();\r
126                 }\r
127 \r
128                 @Override\r
129                 public int size() {\r
130                         return domain.size();\r
131                 }\r
132                 \r
133         };\r
134         \r
135         Set<Range> rangeSet = new AbstractSet<Range>() {\r
136 \r
137                 public boolean add(Range e) {\r
138                         if(range.containsKey(e))\r
139                                 return false;\r
140                         Link<Domain,Range> link = new Link<Domain,Range>(null, null, e);\r
141                         range.put(e, link);\r
142                         modifiedRangeLinks.add(link);\r
143                         return true;\r
144                 }\r
145                 \r
146                 public boolean contains(Object o) {\r
147                         return range.contains(o);\r
148                 }\r
149                 \r
150                 public boolean remove(Object o) {\r
151                         Link<Domain,Range> link = range.remove(o);                      \r
152                         if(link == null)\r
153                                 return false;\r
154                         removeLink(link);\r
155                         if(link.domainElement != null)\r
156                                 domain.remove(link.domainElement);\r
157                         return true;\r
158                 }\r
159                 \r
160                 @Override\r
161                 public Iterator<Range> iterator() {\r
162                     // FIXME does not implement Iterator.remove correctly\r
163                         return range.keySet().iterator();\r
164                 }\r
165 \r
166                 @Override\r
167                 public int size() {\r
168                         return range.size();\r
169                 }\r
170                 \r
171         };\r
172         \r
173         class DomainToRange implements IForwardMapping<Domain, Range> {\r
174 \r
175                 ReadGraph g;\r
176                 \r
177                 public DomainToRange(ReadGraph g) {\r
178                         this.g = g;\r
179                 }\r
180 \r
181                 @Override\r
182                 public Range get(Domain element)  {\r
183                         Link<Domain,Range> link = domain.get(element);\r
184                         if (link != null)\r
185                                 return link.rangeElement;\r
186                         return null;\r
187                         \r
188                 }\r
189                 \r
190                 @Override\r
191                 public Range map(ReadGraph graph, Domain element)\r
192                                 throws MappingException {\r
193                         Link<Domain,Range> link = domain.get(element);\r
194                         if(link == null) {\r
195                             link = new Link<Domain,Range>(null, element, null);\r
196                     link.domainModified = true;\r
197                     modifiedDomainLinks.add(link);\r
198                             domain.put(element, link);           \r
199                             createRange(g, link);       \r
200                         }\r
201                         else if(link.type == null) \r
202                                 createRange(g, link);\r
203             return link.rangeElement;\r
204                 }\r
205                 \r
206                 @Override\r
207                 public Set<Domain> getDomain() {\r
208                         return domain.keySet();\r
209                 }\r
210                 \r
211         };\r
212         \r
213         class RangeToDomain extends DomainToRange implements IBackwardMapping<Domain, Range> {\r
214 \r
215                 WriteGraph g;\r
216                 \r
217                 public RangeToDomain(WriteGraph g) {\r
218                         super(g);\r
219                         this.g = g;\r
220                 }\r
221                 @Override\r
222                 public Domain inverseGet(Range element) {\r
223                         \r
224                         Link<Domain,Range> link = range.get(element);\r
225                         if(link != null)\r
226                                 return link.domainElement;\r
227                         return null;\r
228                 }\r
229                 \r
230                 @Override\r
231                 public Domain inverseMap(WriteGraph graph, Range element)\r
232                                 throws MappingException {\r
233                         Link<Domain,Range> link = range.get(element);\r
234                         if(link == null) {\r
235                             link = new Link<Domain,Range>(null, null, element);\r
236                             link.rangeModified = true;\r
237                 modifiedRangeLinks.add(link);\r
238                             range.put(element, link);\r
239                             createDomain(g, link);                              \r
240                         }\r
241                         else if(link.type == null)\r
242                                 createDomain(g, link);\r
243                         return link.domainElement;\r
244                 }\r
245                 \r
246                 \r
247                 @Override\r
248                 public Set<Range> getRange() {\r
249                         return range.keySet();\r
250                 }\r
251         };\r
252         \r
253         @Override\r
254         public Set<Domain> getDomain() {\r
255                 return domainSet;\r
256         }\r
257         \r
258         @Override\r
259         public Set<Range> getRange() {\r
260                 return rangeSet;\r
261         }\r
262         \r
263         \r
264         @Override\r
265         public synchronized Collection<Domain> updateDomain(WriteGraph g) throws MappingException {\r
266             LOGGER.info("Mapping.updateDomain");\r
267                 RangeToDomain map = new RangeToDomain(g);\r
268                 ArrayList<Domain> updated = new ArrayList<Domain>();\r
269                 while(!modifiedRangeLinks.isEmpty()) {\r
270                     LOGGER.info("    modifiedRangeLinks.size() = " + modifiedRangeLinks.size());\r
271                     \r
272                         Link<Domain,Range> link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1);\r
273                         link.rangeModified = false;\r
274                         /*if(link.domainModified) {\r
275                                 link.domainModified = false;\r
276                                 modifiedDomainLinks.remove(link);\r
277                         }*/\r
278                         \r
279                         if(link.type == null) {\r
280                                 createDomain(g, link);\r
281                         }\r
282                         \r
283                         if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement))\r
284                                 updated.add(link.domainElement);\r
285                 }       \r
286                 if (listensDomain)\r
287                         updateRange(g); //FIXME: without this listening would stop. \r
288                 return updated;\r
289         }\r
290         \r
291         @Override\r
292         public synchronized Collection<Range> updateRange(ReadGraph g) throws MappingException {\r
293             LOGGER.info("Mapping.updateRange");\r
294                 DomainToRange map = new DomainToRange(g);\r
295                 ArrayList<Range> updated = new ArrayList<Range>();\r
296                 while(!modifiedDomainLinks.isEmpty()) {             \r
297                     LOGGER.info("    modifiedDomainLinks.size() = " + modifiedDomainLinks.size());\r
298                     \r
299                         Link<Domain,Range> link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1);\r
300                         link.domainModified = false;\r
301                         /*if(link.rangeModified) {\r
302                                 link.rangeModified = false;\r
303                                 modifiedRangeLinks.remove(link);\r
304                         }*/\r
305                         \r
306                         if(link.type == null) {\r
307                                 createRange(g, link);\r
308                         }\r
309                         \r
310                         if(listensDomain) {\r
311                             RangeUpdateRequest<Domain,Range> request = new RangeUpdateRequest<Domain,Range>(link, map, this);\r
312                             try {\r
313                     g.syncRequest(request, request);\r
314                 } catch (DatabaseException e) {\r
315                     throw new MappingException(e);\r
316                 }\r
317                             // TODO check if really modified\r
318                             updated.add(link.rangeElement);\r
319                         }\r
320                         else\r
321                             if(link.type.updateRange(g, map, link.domainElement, link.rangeElement))\r
322                                 updated.add(link.rangeElement);\r
323                 }       \r
324                 return updated;\r
325         }\r
326 \r
327         @Override\r
328         public Range get(Domain domainElement) {\r
329                 Link<Domain,Range> link = domain.get(domainElement);\r
330                 if(link == null)\r
331                         return null;\r
332                 return link.rangeElement;\r
333         }\r
334 \r
335         @Override\r
336         public Domain inverseGet(Range rangeElement) {\r
337                 Link<Domain,Range> link = range.get(rangeElement);\r
338                 if(link == null)\r
339                         return null;\r
340                 return link.domainElement;\r
341         }\r
342 \r
343         @Override\r
344         public Domain inverseMap(WriteGraph g, Range rangeElement) throws MappingException {\r
345                 getRange().add(rangeElement);\r
346                 updateDomain(g);\r
347                 return inverseGet(rangeElement);\r
348         }\r
349 \r
350         @Override\r
351         public Range map(ReadGraph g, Domain domainElement) throws MappingException {\r
352                 getDomain().add(domainElement);\r
353                 updateRange(g);\r
354                 return get(domainElement);\r
355         }\r
356 \r
357         void domainModified(Link<Domain,Range> link) {\r
358             if(!link.domainModified) {          \r
359                 synchronized(modifiedDomainLinks) {\r
360                     LOGGER.info("        domainModified for " + link.rangeElement);\r
361                 link.domainModified = true;\r
362                 modifiedDomainLinks.add(link);\r
363                 if(modifiedDomainLinks.size() == 1) {\r
364                     for(IMappingListener listener : listeners)\r
365                         listener.domainModified();\r
366                 }\r
367                 }\r
368         }\r
369         }\r
370         \r
371         @Override\r
372         public void domainModified(Domain domainElement) {\r
373                 Link<Domain,Range> link = domain.get(domainElement);\r
374                 if(link != null)\r
375                     domainModified(link);\r
376         }\r
377 \r
378         void rangeModified(Link<Domain,Range> link) {\r
379             if(!link.rangeModified) {\r
380                 synchronized(modifiedRangeLinks) {\r
381                 link.rangeModified = true;\r
382                 modifiedRangeLinks.add(link);\r
383                 if(modifiedRangeLinks.size() == 1) {\r
384                     for(IMappingListener listener : listeners)\r
385                         listener.rangeModified();\r
386                 }\r
387                 }\r
388         }\r
389         }\r
390         \r
391         @Override\r
392         public void rangeModified(Range rangeElement) {\r
393                 Link<Domain,Range> link = range.get(rangeElement);\r
394                 if(link != null)\r
395                     rangeModified(link);\r
396         }\r
397 \r
398         @Override\r
399         public boolean isDomainModified() {\r
400                 return !modifiedDomainLinks.isEmpty();\r
401         }\r
402 \r
403         @Override\r
404         public boolean isRangeModified() {\r
405                 return !modifiedRangeLinks.isEmpty();\r
406         }\r
407         \r
408         @Override\r
409         public Collection<Domain> getDomainModified() {\r
410                 List<Domain> list = new ArrayList<Domain>(modifiedDomainLinks.size());\r
411                 for (Link<Domain, Range> link : modifiedDomainLinks)\r
412                         list.add(link.domainElement);\r
413                 return list;\r
414                                 \r
415         }\r
416         \r
417         @Override\r
418         public Collection<Range> getRangeModified() {\r
419                 List<Range> list = new ArrayList<Range>(modifiedRangeLinks.size());\r
420                 for (Link<Domain, Range> link : modifiedRangeLinks)\r
421                         list.add(link.rangeElement);\r
422                 return list;\r
423         }\r
424 \r
425         @Override\r
426         public void addMappingListener(IMappingListener listener) {\r
427                 listeners.add(listener);\r
428         }\r
429 \r
430         @Override\r
431         public void removeMappingListener(IMappingListener listener) {\r
432                 listeners.remove(listener);             \r
433         }\r
434 \r
435         @Override\r
436         public Collection<Domain> getConflictingDomainElements() {\r
437                 ArrayList<Domain> result = new ArrayList<Domain>();\r
438                 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {\r
439                         for(Link<Domain,Range> link : modifiedDomainLinks)\r
440                                 if(link.rangeModified)\r
441                                         result.add(link.domainElement);\r
442                 }\r
443                 else {\r
444                         for(Link<Domain,Range> link : modifiedRangeLinks)\r
445                                 if(link.domainModified)\r
446                                         result.add(link.domainElement);\r
447                 }\r
448                 return result;\r
449         }\r
450 \r
451         @Override\r
452         public Collection<Range> getConflictingRangeElements() {\r
453                 ArrayList<Range> result = new ArrayList<Range>();\r
454                 if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) {\r
455                         for(Link<Domain,Range> link : modifiedDomainLinks)\r
456                                 if(link.rangeModified)\r
457                                         result.add(link.rangeElement);\r
458                 }\r
459                 else {\r
460                         for(Link<Domain,Range> link : modifiedRangeLinks)\r
461                                 if(link.domainModified)\r
462                                         result.add(link.rangeElement);\r
463                 }\r
464                 return result;\r
465         }\r
466 \r
467     @Override\r
468     public void dispose() {\r
469         disposed = true;\r
470     }\r
471     \r
472     public boolean isDisposed() {\r
473         return disposed;\r
474     }\r
475         \r
476 }\r