1 package org.simantics.db.layer0.variable;
\r
3 import java.util.Arrays;
\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
25 * Relative Value Indicator is a parent-child reference inside variable-tree
\r
29 * @author toni.kalajainen
\r
31 public class RVI extends Bean {
\r
33 private static final RVIPart[] NONE = {};
\r
35 public RVIPart[] parts;
\r
41 public RVI(Binding rviBinding) {
\r
45 public static RVI empty(Binding rviBinding) {
\r
46 RVI result = new RVI(rviBinding);
\r
47 result.parts = NONE;
\r
51 public boolean isEmpty() {
\r
52 return parts.length == 0;
\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
62 public Variable resolvePossible(ReadGraph graph, Variable base) throws DatabaseException {
\r
63 for(RVIPart part : parts) {
\r
64 base = base.resolvePossible(graph, part);
\r
72 public int hashCode() {
\r
73 return Arrays.hashCode(parts);
\r
77 public boolean equals(Object obj) {
\r
82 if (getClass() != obj.getClass())
\r
84 RVI other = (RVI) obj;
\r
85 return Arrays.equals(parts, other.parts);
\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
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
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
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
109 public UniqueRead<String> asStringRequest(final Resource base) {
\r
110 return new UniqueRead<String>() {
\r
112 public String perform(ReadGraph graph) throws DatabaseException {
\r
113 Variable v = Variables.getConfigurationContext(graph, base);
\r
114 return asString(graph, v);
\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
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
129 public String asPossibleString(ReadGraph graph, Variable base) throws DatabaseException {
\r
131 return asString(graph, base);
\r
132 } catch (DatabaseException e) {
\r
138 * Print as persistent string
\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
152 return sb.toString();
\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
167 * database read handle
\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
175 public String toPossibleString(ReadGraph graph, Variable base) throws DatabaseException {
\r
176 String baseURI = base.getURI(graph);
\r
178 Variable resolved = null;
\r
180 for (; i < parts.length; ++i) {
\r
181 RVIPart p = parts[i];
\r
182 base = base.resolvePossible(graph, p);
\r
187 if (resolved == null)
\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
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
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
214 return sb.toString();
\r
218 * Print for USER. Not the real RVI
\r
222 * @throws AdaptionException
\r
223 * @throws ValidationException
\r
224 * @throws ServiceException
\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
236 } else if (p instanceof GuidRVIPart) {
\r
237 String str = p.toString();
\r
241 return sb.toString();
\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
248 public static class ResourceRVIPart implements RVIPart {
\r
250 public Resource resource;
\r
252 public Role getRole() {
\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
260 this.resource = resource;
\r
263 public String toString() {
\r
264 return role.getIdentifier()+"r"+Long.toString( resource.getResourceId() );
\r
267 public int hashCode() {
\r
268 return resource.hashCode() * 31 + role.hashCode();
\r
271 public boolean equals(Object obj) {
\r
276 if (getClass() != obj.getClass())
\r
278 ResourceRVIPart other = (ResourceRVIPart) obj;
\r
279 return role == other.role && resource.equals(other.resource);
\r
283 public static class StringRVIPart implements RVIPart {
\r
285 public String string;
\r
287 public Role getRole() {
\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
295 this.string = string;
\r
298 public String toString() {
\r
299 return role.getIdentifier()+string;
\r
301 public String toEscapedString() {
\r
302 return role.getIdentifier()+URIStringUtils.escape(string);
\r
305 public int hashCode() {
\r
306 return string.hashCode() * 31 + role.hashCode();
\r
309 public boolean equals(Object obj) {
\r
314 if (getClass() != obj.getClass())
\r
316 StringRVIPart other = (StringRVIPart) obj;
\r
317 return role == other.role && string.equals(other.string);
\r
321 public static class GuidRVIPart implements RVIPart {
\r
323 public long mostSignificant;
\r
324 public long leastSignificant;
\r
325 public transient Resource resource;
\r
327 public Role getRole() {
\r
330 public GuidRVIPart() {}
\r
331 public GuidRVIPart(Role role, Resource resource, long mostSignificant, long leastSignificant) {
\r
333 this.resource = resource;
\r
334 this.mostSignificant = mostSignificant;
\r
335 this.leastSignificant = leastSignificant;
\r
338 public String toString() {
\r
339 return role.getIdentifier()+mostSignificant+":"+leastSignificant;
\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
346 public int hashCode() {
\r
347 return (longHashCode(leastSignificant) * 51 + longHashCode(mostSignificant)) * 31 + role.hashCode();
\r
350 * TODO: remove this when switched into Java 1.8
\r
352 private static int longHashCode(long value) {
\r
353 return (int)(value ^ (value >>> 32));
\r
357 public boolean equals(Object obj) {
\r
362 if (getClass() != obj.getClass())
\r
364 GuidRVIPart other = (GuidRVIPart) obj;
\r
365 return role == other.role && leastSignificant == other.leastSignificant && mostSignificant == other.mostSignificant;
\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
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
381 char c = str.charAt(pos);
\r
384 role = Role.PROPERTY;
\r
385 } else if (c=='/') {
\r
391 int e1 = str.indexOf('#', pos);
\r
392 int e2 = str.indexOf('/', pos);
\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
400 long res = (int) Long.parseLong(x);
\r
401 Resource r = support.getResource( res );
\r
402 rb.append( role, r );
\r
405 } catch (NumberFormatException nfe) {
\r
406 } catch (DatabaseException e) {
\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
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
423 } catch (NumberFormatException nfe) {
\r
424 } catch (DatabaseException e) {
\r
429 String text = URIStringUtils.unescape( str.substring(pos, end) );
\r
431 rb.append( role, text );
\r