Provide classifications and datatype for Variable-based drag sources
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / selection / WorkbenchSelectionUtils.java
1 package org.simantics.ui.selection;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Set;
8 import java.util.stream.Collectors;
9
10 import org.eclipse.core.commands.ExecutionEvent;
11 import org.eclipse.jface.viewers.ISelection;
12 import org.eclipse.ui.handlers.HandlerUtil;
13 import org.simantics.Simantics;
14 import org.simantics.databoard.type.Datatype;
15 import org.simantics.db.ReadGraph;
16 import org.simantics.db.RequestProcessor;
17 import org.simantics.db.Resource;
18 import org.simantics.db.common.primitiverequest.IsInstanceOf;
19 import org.simantics.db.common.primitiverequest.Supertypes;
20 import org.simantics.db.common.primitiverequest.Types;
21 import org.simantics.db.exception.DatabaseException;
22 import org.simantics.db.layer0.request.PossibleGUID;
23 import org.simantics.db.layer0.request.PossibleURI;
24 import org.simantics.db.layer0.request.PossibleVariableGUID;
25 import org.simantics.db.layer0.request.PossibleVariableRepresents;
26 import org.simantics.db.layer0.request.VariableRead;
27 import org.simantics.db.layer0.request.VariableURI;
28 import org.simantics.db.layer0.variable.Variable;
29 import org.simantics.utils.ui.ISelectionUtils;
30
31 public class WorkbenchSelectionUtils {
32
33         public static Resource getPossibleResource(ExecutionEvent event) throws DatabaseException {
34                 ISelection selection = HandlerUtil.getCurrentSelection(event);
35                 return getPossibleResource(selection);
36         }
37         
38         public static Variable getPossibleVariable(ExecutionEvent event) throws DatabaseException {
39                 ISelection selection = HandlerUtil.getCurrentSelection(event);
40                 return getPossibleVariable(selection);
41         }
42         
43         public static List<WorkbenchSelectionElement> getWorkbenchSelectionElements(ISelection selection) {
44             return ISelectionUtils.filterSelection(selection, WorkbenchSelectionElement.class);
45         }
46
47         public static String getPossibleJSON(Object selection) throws DatabaseException {
48                 WorkbenchSelectionElement element = getPossibleWorkbenchSelectionElement(selection);
49                 return element != null ? getPossibleJSON(element) : null;
50         }
51
52         public static Resource getPossibleResource(Object selection) throws DatabaseException {
53                 WorkbenchSelectionElement element = getPossibleWorkbenchSelectionElement(selection);
54                 return element != null ? getPossibleResource(element) : null;
55         }
56
57         public static Variable getPossibleVariable(Object selection) throws DatabaseException {
58                 WorkbenchSelectionElement element = getPossibleWorkbenchSelectionElement(selection);
59                 return element != null ? getPossibleVariable(element) : null;
60         }
61
62         public static Resource getPossibleResourceFromSelection(RequestProcessor processor, Object selection) throws DatabaseException {
63                 WorkbenchSelectionElement element = getPossibleWorkbenchSelectionElement(selection);
64                 return element != null ? getPossibleResource(processor, element) : null;
65         }
66
67         public static Variable getPossibleVariableFromSelection(RequestProcessor processor, Object selection) throws DatabaseException {
68                 WorkbenchSelectionElement element = getPossibleWorkbenchSelectionElement(selection);
69                 return element != null ? getPossibleVariable(processor, element) : null;
70         }
71
72         public static Variable getPossibleVariable(Object[] selection) throws DatabaseException {
73                 if(selection.length != 1) return null;
74                 if(!(selection[0] instanceof WorkbenchSelectionElement)) return null;
75                 return getPossibleVariable((WorkbenchSelectionElement)selection[0]);
76         }
77
78         public static <T> T getPossibleElement(Object[] selection, WorkbenchSelectionContentType<T> contentType) throws DatabaseException {
79                 if(selection.length != 1) return null;
80                 if(!(selection[0] instanceof WorkbenchSelectionElement)) return null;
81                 WorkbenchSelectionElement wse = (WorkbenchSelectionElement)selection[0];
82                 return wse.getContent(contentType);
83         }
84
85         public static String getPossibleJSON(WorkbenchSelectionElement wse) throws DatabaseException {
86                 return getPossibleJSON(Simantics.getSession(), wse);
87         }
88
89         public static Resource getPossibleResource(WorkbenchSelectionElement wse) throws DatabaseException {
90                 return getPossibleResource(Simantics.getSession(), wse);
91         }
92
93         public static Variable getPossibleVariable(WorkbenchSelectionElement wse) throws DatabaseException {
94                 return getPossibleVariable(Simantics.getSession(), wse);
95         }
96
97         public static Variable getPossibleVariableSCL(ReadGraph graph, WorkbenchSelectionElement wse) throws DatabaseException {
98                 return getPossibleVariable(graph, wse);
99         }
100
101         public static Resource getPossibleResourceSCL(ReadGraph graph, WorkbenchSelectionElement wse) throws DatabaseException {
102                 return getPossibleResource(graph, wse);
103         }
104
105         public static String getPossibleJSON(RequestProcessor processor, Object input) throws DatabaseException {
106                 Variable var = getPossibleVariable(processor, input);
107                 Resource res = getPossibleResource(processor, input);
108                 String typesStr = getTypeResourceString(processor, res, var);
109                 if(var != null) {
110                         String uri = processor.syncRequest(new VariableURI(var));
111                         String guid = processor.syncRequest(new PossibleVariableGUID(var));
112                         
113                         Set<String> classifications = processor.syncRequest(new VariableRead<Set<String>>(var) {
114                                 @Override
115                                 public Set<String> perform(ReadGraph graph) throws DatabaseException {
116                                         return var.getClassifications(graph);
117                                 }
118                         });
119                         String classificationsStr = toJSONStringArray(new ArrayList<>(classifications));
120                         
121                         Datatype datatype = processor.syncRequest(new VariableRead<Datatype>(var) {
122                                 @Override
123                                 public Datatype perform(ReadGraph graph) throws DatabaseException {
124                                         return var.getPossibleDatatype(graph);
125                                 }
126                         });
127                         
128                         return toJSONObjectString(
129                                         "type", "\"Variable\"",
130                                         "uri", safeQuotedString(uri),
131                                         "guid", safeQuotedString(guid),
132                                         "resourceId", res == null ? "" : Long.toString(res.getResourceId()),
133                                         "typeResources", typesStr,
134                                         "classifications", classificationsStr,
135                                         "datatype", safeQuotedString(datatype != null ? datatype.getClass().getName() : null));
136                 }
137                 if(res != null) {
138                     String uri = processor.syncRequest(new PossibleURI(res));
139                     String guid = processor.syncRequest(new PossibleGUID(res));
140                         return toJSONObjectString(
141                                         "type", "\"Resource\"",
142                                         "uri", safeQuotedString(uri),
143                                         "guid", safeQuotedString(guid),
144                                         "resourceId", Long.toString(res.getResourceId()),
145                                         "typeResources", typesStr);
146                 }
147                 return "{ \"type\": \"Unknown\" }";
148         }
149
150         public static Resource getPossibleResource(RequestProcessor processor, Object input) throws DatabaseException {
151                 return getPossibleResource(processor, input, null);
152         }
153
154         public static Resource getPossibleResource(RequestProcessor processor, Object input, Resource type) throws DatabaseException {
155             if(input instanceof Collection && !((Collection<?>)input).isEmpty()) {
156                 Object element = ((Collection<?>)input).iterator().next();
157                 if(element instanceof Resource)
158                     return (Resource)element;
159             }
160                 Resource resource = getPossibleElement(input, new AnyResource(processor));
161                 if(resource == null) return resource;
162                 if(type != null) {
163                         if(processor.sync(new IsInstanceOf(resource, type))) return resource;
164                         else return null;
165                 }
166                 return resource;
167         }
168
169         public static Variable getPossibleVariable(RequestProcessor processor, Object input) throws DatabaseException {
170                 return getPossibleElement(input, new AnyVariable(processor));
171         }
172
173         public static <T> T getPossibleElement(Object input, WorkbenchSelectionContentType<T> contentType) {
174                 Object single = getPossibleSingleElement(input);
175                 if(single == null) return null;
176                 if(single instanceof WorkbenchSelectionElement) {
177                         WorkbenchSelectionElement element = (WorkbenchSelectionElement)single;
178                         return (T)element.getContent(contentType);
179                 }
180                 return null;
181         }
182         
183         public static WorkbenchSelectionElement getPossibleSelectionElement(Object input) {
184         if(input instanceof WorkbenchSelectionElement) return (WorkbenchSelectionElement)input;
185         if(input instanceof Collection) {
186                 Collection<?> c = (Collection<?>)input;
187                 if(c.size() == 1) {
188                         Object o = c.iterator().next();
189                         if(o instanceof WorkbenchSelectionElement) return (WorkbenchSelectionElement)o;
190                 }
191         }
192         return null;
193         }
194
195         // Internal helpers
196         
197     private static Object getPossibleSingleElement(Object input) {
198         if(input instanceof WorkbenchSelectionElement) return input;
199         if(input instanceof Collection) {
200                 Collection<?> c = (Collection<?>)input;
201                 if(c.size() == 1) return c.iterator().next();
202         } 
203         return null;
204     }   
205
206     private static WorkbenchSelectionElement getPossibleWorkbenchSelectionElement(Object selection) {
207         return getPossibleObject(selection, WorkbenchSelectionElement.class);
208     }
209
210     @SuppressWarnings("unchecked")
211     private static <T> T getPossibleObject(Object selection, Class<T> clazz) {
212         return clazz.isInstance(selection)
213                 ? (T) selection
214                 : ISelectionUtils.filterSingleSelection(selection, clazz);
215     }
216
217     private static class PossibleVariableType extends VariableRead<Resource> {
218
219         public PossibleVariableType(Variable var) {
220             super(var);
221         }
222
223         @Override
224         public Resource perform(ReadGraph graph) throws DatabaseException {
225             return variable.getPossibleType(graph);
226         }
227
228     }
229
230         private static String toJSONObjectString(String... keyValuePairs) {
231                 int len = keyValuePairs.length;
232                 assert (len & 1) == 0;
233                 StringBuilder sb = new StringBuilder(128);
234                 sb.append("{ ");
235                 int entryCount = 0;
236                 for (int i = 0; i < len; i += 2) {
237                         String value = keyValuePairs[i+1];
238                         if (value != null && !value.isEmpty()) {
239                                 if (entryCount > 0)
240                                         sb.append(", ");
241                                 sb.append("\"").append(keyValuePairs[i]).append("\": ").append(value);
242                                 ++entryCount;
243                         }
244                 }
245                 sb.append(" }");
246                 return sb.toString();
247         }
248
249         private static String escapeQuotes(String s) {
250                 return s.indexOf('"') >= 0 ? s.replaceAll("\"", "\\\\\"") : s;
251         }
252
253         private static String safeQuotedString(String s) {
254                 return s != null && !s.isEmpty() ? "\"" + escapeQuotes(s) + "\"" : "";
255         }
256
257         private static String toJSONStringArray(List<String> strings) throws DatabaseException {
258                 // Sort the type strings to produce stable results
259                 if (strings.isEmpty())
260                         return "";
261                 return strings.stream()
262                                 .map(WorkbenchSelectionUtils::escapeQuotes)
263                                 .sorted()
264                                 .collect(Collectors.joining("\", \"", "[\"", "\"]"));
265         }
266
267         private static String getTypeResourceString(RequestProcessor processor, Resource r, Variable v) throws DatabaseException {
268                 return toJSONStringArray(
269                                 toPossibleURIs(processor,
270                                                 getTypes(processor, r, v)));
271         }
272
273         private static Set<Resource> getTypes(RequestProcessor processor, Resource r, Variable v) throws DatabaseException {
274                 if (r == null && v != null)
275                         r = processor.syncRequest(new PossibleVariableRepresents(v));
276                 if (r != null)
277                         return processor.syncRequest(new Types(r));
278                 r = v != null ? processor.syncRequest(new PossibleVariableType(v)) : null;
279                 return r != null ? processor.syncRequest(new Supertypes(r)) : Collections.emptySet();
280         }
281
282         private static List<String> toPossibleURIs(RequestProcessor processor, Collection<Resource> resources) throws DatabaseException {
283                 if (resources.isEmpty())
284                         return Collections.emptyList();
285                 List<String> result = new ArrayList<>(resources.size());
286                 for (Resource r : resources) {
287                         String uri = processor.syncRequest(new PossibleURI(r));
288                         if (uri != null)
289                                 result.add(uri);
290                 }
291                 return result;
292         }
293
294 }