]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/RVI.java
Merge commit 'ad8333027322fda6b9a8a524c7a7e15a54c52f38'
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / RVI.java
1 package org.simantics.db.layer0.variable;\r
2 \r
3 import java.util.Arrays;\r
4 \r
5 import org.simantics.databoard.Bindings;\r
6 import org.simantics.databoard.Databoard;\r
7 import org.simantics.databoard.annotations.Union;\r
8 import org.simantics.databoard.binding.Binding;\r
9 import org.simantics.databoard.util.Bean;\r
10 import org.simantics.databoard.util.URIStringUtils;\r
11 import org.simantics.db.ReadGraph;\r
12 import org.simantics.db.RequestProcessor;\r
13 import org.simantics.db.Resource;\r
14 import org.simantics.db.common.request.UniqueRead;\r
15 import org.simantics.db.common.utils.NameUtils;\r
16 import org.simantics.db.exception.AdaptionException;\r
17 import org.simantics.db.exception.DatabaseException;\r
18 import org.simantics.db.exception.ServiceException;\r
19 import org.simantics.db.exception.ValidationException;\r
20 import org.simantics.db.layer0.variable.Variables.Role;\r
21 import org.simantics.db.service.SerialisationSupport;\r
22 import org.simantics.layer0.Layer0;\r
23 \r
24 /**\r
25  * Relative Value Indicator is a parent-child reference inside variable-tree \r
26  * address space. \r
27  *  \r
28  * @See RVIBuilder\r
29  * @author toni.kalajainen\r
30  */\r
31 public class RVI extends Bean {\r
32         \r
33         private static final RVIPart[] NONE = {};\r
34         \r
35         public RVIPart[] parts;\r
36         \r
37         public RVI() {\r
38                 super();\r
39         }\r
40         \r
41         public RVI(Binding rviBinding) {\r
42                 super(rviBinding);\r
43         }\r
44         \r
45         public static RVI empty(Binding rviBinding) {\r
46                 RVI result = new RVI(rviBinding);\r
47                 result.parts = NONE;\r
48                 return result;\r
49         }\r
50         \r
51         public boolean isEmpty() {\r
52                 return parts.length == 0;\r
53         }\r
54         \r
55         public Variable resolve(ReadGraph graph, Variable base) throws DatabaseException {\r
56                 for(RVIPart part : parts) {\r
57                         base = base.resolve(graph, part);\r
58                 }\r
59                 return base;\r
60         }\r
61 \r
62         public Variable resolvePossible(ReadGraph graph, Variable base) throws DatabaseException {\r
63                 for(RVIPart part : parts) {\r
64                         base = base.resolvePossible(graph, part);\r
65                         if (base == null)\r
66                                 return null;\r
67                 }\r
68                 return base;\r
69         }\r
70 \r
71         @Override\r
72         public int hashCode() {\r
73                 return Arrays.hashCode(parts);\r
74         }\r
75 \r
76         @Override\r
77         public boolean equals(Object obj) {\r
78                 if (this == obj)\r
79                         return true;\r
80                 if (obj == null)\r
81                         return false;\r
82                 if (getClass() != obj.getClass())\r
83                         return false;\r
84                 RVI other = (RVI) obj;\r
85                 return Arrays.equals(parts, other.parts);\r
86         }\r
87 \r
88         /**\r
89          * Returns a string representation of the all the parts of this RVI for\r
90          * visualization purposes. This representation of the RVI does not withstand\r
91          * moving the RVI-referenced resource between model composites unlike the\r
92          * databoard serialization of this class.\r
93          * \r
94          * <p>\r
95          * Implementation takes the RVI part of a Variable resolved using this RVI\r
96          * and the specified Variable as a base for the resolution.\r
97          * \r
98          * @param graph transaction handle\r
99          * @param base base resource to use for resolving the variable\r
100          * @return this RVI as a String\r
101          * @throws DatabaseException\r
102          */\r
103         public String asString(ReadGraph graph, Variable base) throws DatabaseException {\r
104                 String baseURI = base.getURI(graph);\r
105                 Variable resolved = resolve(graph, base);\r
106                 return resolved.getURI(graph).substring(baseURI.length());\r
107         }\r
108 \r
109         public UniqueRead<String> asStringRequest(final Resource base) {\r
110                 return new UniqueRead<String>() {\r
111                         @Override\r
112                         public String perform(ReadGraph graph) throws DatabaseException {\r
113                                 Variable v = Variables.getConfigurationContext(graph, base);\r
114                                 return asString(graph, v);\r
115                         }\r
116                 };\r
117         }\r
118 \r
119             /**\r
120      * Works like {@link #asString(ReadGraph, Variable)} but returns\r
121      * <code>null</code> if resolution of the string form fails because of the\r
122      * database contents.\r
123      * \r
124      * @param graph transaction handle\r
125      * @param base base resource to use for resolving the variable\r
126      * @return this RVI as a String\r
127      * @throws DatabaseException\r
128      */\r
129         public String asPossibleString(ReadGraph graph, Variable base) throws DatabaseException {\r
130                 try {\r
131                         return asString(graph, base);\r
132                 } catch (DatabaseException e) {\r
133                         return null;\r
134                 }\r
135         }\r
136 \r
137         /**\r
138          * Print as persistent string\r
139          */\r
140         @Override\r
141         public String toString() {\r
142                 StringBuilder sb = new StringBuilder();\r
143                 for (RVIPart p : parts) {\r
144                         if (p instanceof ResourceRVIPart) {\r
145                                 sb.append(p.toString());\r
146                         } else if (p instanceof StringRVIPart) {\r
147                                 sb.append(((StringRVIPart) p).toEscapedString());\r
148                         } else if (p instanceof GuidRVIPart) {\r
149                                 sb.append(((GuidRVIPart) p).toEscapedString());\r
150                         }\r
151                 }\r
152                 return sb.toString();\r
153         }\r
154 \r
155         /**\r
156          * Resolves this RVI into a string with a best effort method that tries to\r
157          * resolve the RVI parts into variables for as long as it can. After\r
158          * resolution is finished the remaining RVI parts are simply concatenated\r
159          * into the result string as strings without resolving them into variables.\r
160          * This is different from {@link #asString(ReadGraph, Variable)} or\r
161          * {@link #asPossibleString(ReadGraph, Variable)} in that it doesn't demand\r
162          * that the RVI resolves completely. Still at least the first RVI part must\r
163          * resolve, otherwise <code>null</code> is returned, since this means that\r
164          * the variable no longer exists at all.\r
165          * \r
166          * @param graph\r
167          *            database read handle\r
168          * @param base\r
169          *            the base of resolution for the RVI\r
170          * @return The RVI of the referenced entity as a string or <code>null</code>\r
171          *         if zero parts of this RVI can be resolved, i.e. the resources\r
172          *         referenced by it no longer exist.\r
173          * @throws DatabaseException\r
174          */\r
175         public String toPossibleString(ReadGraph graph, Variable base) throws DatabaseException {\r
176                 String baseURI = base.getURI(graph);\r
177 \r
178                 Variable resolved = null;\r
179                 int i = 0;\r
180                 for (; i < parts.length; ++i) {\r
181                         RVIPart p = parts[i];\r
182                         base = base.resolvePossible(graph, p);\r
183                         if (base == null)\r
184                                 break;\r
185                         resolved = base;\r
186                 }\r
187                 if (resolved == null)\r
188                         return null;\r
189 \r
190                 StringBuilder sb = new StringBuilder();\r
191                 String resolvedURI = resolved.getURI(graph);\r
192                 sb.append(resolvedURI, baseURI.length(), resolvedURI.length());\r
193                 if (i < parts.length) {\r
194                         // The tail didn't resolve into a Variable synchronously\r
195                         // so let's just concatenate the rest by hand into the\r
196                         // result string.\r
197                         Layer0 L0 = Layer0.getInstance(graph);\r
198                         for (; i < parts.length; ++i) {\r
199                                 RVIPart p = parts[i];\r
200                                 if (p instanceof ResourceRVIPart) {\r
201                                         Resource r = ((ResourceRVIPart) p).resource;\r
202                                         String str = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);\r
203                                         if (str == null)\r
204                                                 return null;\r
205                                         sb.append(p.getRole().getIdentifier()).append(URIStringUtils.escape(str));\r
206                                 } else if (p instanceof StringRVIPart) {\r
207                                         sb.append(p.getRole().getIdentifier()).append(URIStringUtils.escape(((StringRVIPart) p).string));\r
208                                 } else if (p instanceof GuidRVIPart) {\r
209                                         sb.append(p.getRole().getIdentifier()).append(((GuidRVIPart)p).mostSignificant).append(":").append(((GuidRVIPart)p).leastSignificant);\r
210                                 }\r
211                         }\r
212                 }\r
213 \r
214                 return sb.toString();\r
215         }\r
216 \r
217         /**\r
218          * Print for USER. Not the real RVI\r
219          * \r
220          * @param graph\r
221          * @return\r
222          * @throws AdaptionException\r
223          * @throws ValidationException\r
224          * @throws ServiceException\r
225          */\r
226         public String toString(ReadGraph graph) throws DatabaseException {\r
227                 StringBuilder sb = new StringBuilder();\r
228                 for (RVIPart p : parts) {\r
229                         if (p instanceof ResourceRVIPart) {\r
230                                 Resource r = ((ResourceRVIPart) p).resource;\r
231                                 String str = NameUtils.getSafeName(graph, r);\r
232                                 sb.append(p.getRole().getIdentifier()).append(str);\r
233                         } else if (p instanceof StringRVIPart) {\r
234                                 String str = p.toString();\r
235                                 sb.append(str);\r
236                         } else if (p instanceof GuidRVIPart) {\r
237                                 String str = p.toString();\r
238                                 sb.append(str);\r
239                         }\r
240                 }\r
241                 return sb.toString();\r
242         }\r
243         \r
244         // Enumeration | ResourceRVIPart | StringRVIPart | GuidRVIPart\r
245         @Union({ResourceRVIPart.class, StringRVIPart.class, GuidRVIPart.class})\r
246         public static interface RVIPart { public Role getRole(); }\r
247         \r
248         public static class ResourceRVIPart implements RVIPart {\r
249                 public Role role;\r
250                 public Resource resource;\r
251                 @Override\r
252                 public Role getRole() {\r
253                         return role;\r
254                 }\r
255                 public ResourceRVIPart() {}\r
256                 public ResourceRVIPart(Role role, Resource resource) {\r
257                     if (resource == null)\r
258                         throw new NullPointerException("null resource");\r
259                         this.role = role;\r
260                         this.resource = resource;\r
261                 }\r
262                 @Override\r
263                 public String toString() {\r
264                         return role.getIdentifier()+"r"+Long.toString( resource.getResourceId() );\r
265                 }\r
266                 @Override\r
267                 public int hashCode() {\r
268                         return resource.hashCode() * 31 + role.hashCode();\r
269                 }\r
270                 @Override\r
271                 public boolean equals(Object obj) {\r
272                         if (this == obj)\r
273                                 return true;\r
274                         if (obj == null)\r
275                                 return false;\r
276                         if (getClass() != obj.getClass())\r
277                                 return false;\r
278                         ResourceRVIPart other = (ResourceRVIPart) obj;\r
279                         return role == other.role && resource.equals(other.resource);\r
280                 }\r
281         }\r
282         \r
283         public static class StringRVIPart implements RVIPart {\r
284                 public Role role;\r
285                 public String string;\r
286                 @Override\r
287                 public Role getRole() {\r
288                         return role;\r
289                 }\r
290                 public StringRVIPart() {}\r
291                 public StringRVIPart(Role role, String string) {\r
292                         if (string == null)\r
293                                 throw new NullPointerException("null string");\r
294                         this.role = role;\r
295                         this.string = string;\r
296                 }\r
297                 @Override\r
298                 public String toString() {\r
299                         return role.getIdentifier()+string;\r
300                 }\r
301                 public String toEscapedString() {\r
302                         return role.getIdentifier()+URIStringUtils.escape(string);\r
303                 }\r
304                 @Override\r
305                 public int hashCode() {\r
306                         return string.hashCode() * 31 + role.hashCode();\r
307                 }\r
308                 @Override\r
309                 public boolean equals(Object obj) {\r
310                         if (this == obj)\r
311                                 return true;\r
312                         if (obj == null)\r
313                                 return false;\r
314                         if (getClass() != obj.getClass())\r
315                                 return false;\r
316                         StringRVIPart other = (StringRVIPart) obj;\r
317                         return role == other.role && string.equals(other.string);\r
318                 }\r
319         }\r
320 \r
321         public static class GuidRVIPart implements RVIPart {\r
322                 public Role role;\r
323                 public long mostSignificant;\r
324                 public long leastSignificant;\r
325                 public transient Resource resource;\r
326                 @Override\r
327                 public Role getRole() {\r
328                         return role;\r
329                 }\r
330                 public GuidRVIPart() {}\r
331                 public GuidRVIPart(Role role, Resource resource, long mostSignificant, long leastSignificant) {\r
332                         this.role = role;\r
333                         this.resource = resource;\r
334                         this.mostSignificant = mostSignificant;\r
335                         this.leastSignificant = leastSignificant;\r
336                 }\r
337                 @Override\r
338                 public String toString() {\r
339                         return role.getIdentifier()+mostSignificant+":"+leastSignificant;\r
340                 }\r
341                 public String toEscapedString() {\r
342                         String rid = resource != null ? Long.toString( resource.getResourceId() ) : "0";\r
343                         return role.getIdentifier()+mostSignificant+":"+leastSignificant+":"+rid;\r
344                 }\r
345                 @Override\r
346                 public int hashCode() {\r
347                         return (longHashCode(leastSignificant) * 51 + longHashCode(mostSignificant)) * 31 + role.hashCode();\r
348                 }\r
349                 /*\r
350                  * TODO: remove this when switched into Java 1.8\r
351                  */\r
352                 private static int longHashCode(long value) {\r
353                 return (int)(value ^ (value >>> 32));\r
354                 }\r
355 \r
356                 @Override\r
357                 public boolean equals(Object obj) {\r
358                         if (this == obj)\r
359                                 return true;\r
360                         if (obj == null)\r
361                                 return false;\r
362                         if (getClass() != obj.getClass())\r
363                                 return false;\r
364                         GuidRVIPart other = (GuidRVIPart) obj;\r
365                         return role == other.role && leastSignificant == other.leastSignificant && mostSignificant == other.mostSignificant;\r
366                 }\r
367                 \r
368         }\r
369 \r
370         public static RVI fromResourceFormat( RequestProcessor proc, String str ) {\r
371                 SerialisationSupport support = proc.getService(SerialisationSupport.class);\r
372                 if (support == null) throw new RuntimeException("No serialization support in Session");\r
373                 Databoard databoard = proc.getService(Databoard.class);\r
374                 if (databoard == null) throw new RuntimeException("No databoard support in Session");\r
375                 \r
376                 Binding rviBinding = databoard.getBindingUnchecked( RVI.class );\r
377                 RVIBuilder rb = new RVIBuilder( rviBinding );\r
378                 int pos = 0, len = str.length();\r
379                 while (pos<str.length()) {\r
380                         Role role = null;\r
381                         char c = str.charAt(pos);\r
382                         if (c=='#') {\r
383                                 pos++;\r
384                                 role = Role.PROPERTY;\r
385                         } else if (c=='/') {\r
386                                 pos++;\r
387                                 role = Role.CHILD;\r
388                         } else {\r
389                                 role = Role.CHILD;\r
390                         }\r
391                         int e1 = str.indexOf('#', pos);\r
392                         int e2 = str.indexOf('/', pos);\r
393                         e1 = e1<0?len:e1;\r
394                         e2 = e2<0?len:e2;\r
395                         int end = e1<e2?e1:e2;\r
396                         if (str.charAt(pos) == 'r') {\r
397                                 String x = str.substring(pos+1, end);\r
398                                 if (!x.isEmpty()) {\r
399                                         try {\r
400                                                 long res = (int) Long.parseLong(x);\r
401                                                 Resource r = support.getResource( res );\r
402                                                 rb.append( role, r );\r
403                                                 pos = end;\r
404                                                 continue;\r
405                                         } catch (NumberFormatException nfe) {\r
406                                         } catch (DatabaseException e) {\r
407                                         }\r
408                                 }\r
409                         }\r
410                         if (str.indexOf(":", pos+1) > -1) {\r
411                             String x = str.substring(pos+1, end);\r
412                             if (!x.isEmpty()) {\r
413                                 String[] parts = x.split(":");\r
414                                 if (parts.length == 3) {\r
415                                         try {\r
416                                                 long res = (int) Long.parseLong(parts[2]);\r
417                                                 long mostSignificant = Long.parseLong(parts[0]);\r
418                                                 long leastSignificant = Long.parseLong(parts[1]);\r
419                                                 Resource r = support.getResource( res );\r
420                                                 rb.append( role, r, mostSignificant, leastSignificant);\r
421                                                 pos = end;\r
422                                                 continue;\r
423                                         } catch (NumberFormatException nfe) {\r
424                                         } catch (DatabaseException e) {\r
425                                         }\r
426                                 }\r
427                 }\r
428                         }\r
429                         String text = URIStringUtils.unescape( str.substring(pos, end) );\r
430                         pos = end;\r
431                         rb.append( role, text );\r
432                 }\r
433                 return rb.toRVI();\r
434         }\r
435         \r
436 }\r