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