]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.debug.ui/src/org/simantics/debug/ui/VariableDebugger.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.debug.ui / src / org / simantics / debug / ui / VariableDebugger.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 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.debug.ui;\r
13 \r
14 import java.io.File;\r
15 import java.io.FileNotFoundException;\r
16 import java.io.IOException;\r
17 import java.lang.reflect.Array;\r
18 import java.net.URL;\r
19 import java.nio.charset.Charset;\r
20 import java.util.ArrayList;\r
21 import java.util.Arrays;\r
22 import java.util.LinkedList;\r
23 import java.util.TreeMap;\r
24 import java.util.concurrent.CopyOnWriteArrayList;\r
25 import java.util.concurrent.atomic.AtomicReference;\r
26 \r
27 import org.eclipse.core.runtime.Assert;\r
28 import org.eclipse.core.runtime.FileLocator;\r
29 import org.eclipse.core.runtime.IPath;\r
30 import org.eclipse.core.runtime.Path;\r
31 import org.eclipse.jface.layout.GridDataFactory;\r
32 import org.eclipse.jface.resource.ColorDescriptor;\r
33 import org.eclipse.jface.resource.JFaceResources;\r
34 import org.eclipse.jface.resource.LocalResourceManager;\r
35 import org.eclipse.swt.SWT;\r
36 import org.eclipse.swt.SWTError;\r
37 import org.eclipse.swt.browser.Browser;\r
38 import org.eclipse.swt.browser.LocationAdapter;\r
39 import org.eclipse.swt.browser.LocationEvent;\r
40 import org.eclipse.swt.dnd.DND;\r
41 import org.eclipse.swt.dnd.DropTarget;\r
42 import org.eclipse.swt.dnd.DropTargetAdapter;\r
43 import org.eclipse.swt.dnd.DropTargetEvent;\r
44 import org.eclipse.swt.dnd.TextTransfer;\r
45 import org.eclipse.swt.dnd.Transfer;\r
46 import org.eclipse.swt.events.DisposeEvent;\r
47 import org.eclipse.swt.events.DisposeListener;\r
48 import org.eclipse.swt.events.KeyAdapter;\r
49 import org.eclipse.swt.events.KeyEvent;\r
50 import org.eclipse.swt.events.SelectionEvent;\r
51 import org.eclipse.swt.events.SelectionListener;\r
52 import org.eclipse.swt.graphics.Color;\r
53 import org.eclipse.swt.graphics.RGB;\r
54 import org.eclipse.swt.layout.GridData;\r
55 import org.eclipse.swt.layout.GridLayout;\r
56 import org.eclipse.swt.widgets.Button;\r
57 import org.eclipse.swt.widgets.Composite;\r
58 import org.eclipse.swt.widgets.Label;\r
59 import org.eclipse.swt.widgets.Text;\r
60 import org.simantics.databoard.type.Datatype;\r
61 import org.simantics.databoard.util.ObjectUtils;\r
62 import org.simantics.db.ReadGraph;\r
63 import org.simantics.db.Resource;\r
64 import org.simantics.db.Session;\r
65 import org.simantics.db.common.ResourceArray;\r
66 import org.simantics.db.common.procedure.adapter.DisposableListener;\r
67 import org.simantics.db.common.request.UnaryRead;\r
68 import org.simantics.db.common.utils.Logger;\r
69 import org.simantics.db.exception.DatabaseException;\r
70 import org.simantics.db.layer0.SelectionHints;\r
71 import org.simantics.db.layer0.request.PossibleURI;\r
72 import org.simantics.db.layer0.request.ResourceURIToVariable;\r
73 import org.simantics.db.layer0.request.VariableURI;\r
74 import org.simantics.db.layer0.variable.AbstractChildVariable;\r
75 import org.simantics.db.layer0.variable.AbstractPropertyVariable;\r
76 import org.simantics.db.layer0.variable.Variable;\r
77 import org.simantics.db.layer0.variable.VariableNode;\r
78 import org.simantics.db.layer0.variable.Variables;\r
79 import org.simantics.db.service.SerialisationSupport;\r
80 import org.simantics.debug.ui.internal.Activator;\r
81 import org.simantics.layer0.Layer0;\r
82 import org.simantics.structural2.variables.Connection;\r
83 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;\r
84 import org.simantics.ui.dnd.LocalObjectTransfer;\r
85 import org.simantics.ui.dnd.ResourceReferenceTransfer;\r
86 import org.simantics.ui.dnd.ResourceTransferUtils;\r
87 import org.simantics.ui.utils.ResourceAdaptionUtils;\r
88 import org.simantics.utils.FileUtils;\r
89 import org.simantics.utils.bytes.Base64;\r
90 import org.simantics.utils.ui.ErrorLogger;\r
91 import org.simantics.utils.ui.ISelectionUtils;\r
92 import org.simantics.utils.ui.PathUtils;\r
93 \r
94 \r
95 /**\r
96  * @author Antti Villberg\r
97  * @author Tuukka Lehtonen\r
98  */\r
99 public class VariableDebugger extends Composite {\r
100 \r
101     public interface HistoryListener {\r
102         void historyChanged();\r
103     }\r
104 \r
105     private final static String                         DEFAULT_DEBUGGER_CSS_FILE = "debugger.css";\r
106     private final static String                         DEFAULT_DEBUGGER_CSS_PATH = "css/" + DEFAULT_DEBUGGER_CSS_FILE;\r
107 \r
108     private static int                                  RESOURCE_NAME_MAX_LENGTH  = 1000;\r
109 \r
110         private final Charset                               utf8 = Charset.forName("UTF-8");\r
111 \r
112     private final LocalResourceManager                  resourceManager;\r
113 \r
114     private String                                      cssPath;\r
115 \r
116     private Text                                        updateTriggerCounter; \r
117     private Browser                                     browser;\r
118     private final ColorDescriptor                       green = ColorDescriptor.createFrom(new RGB(0x57, 0xbc, 0x95));\r
119 \r
120     private final LinkedList<String>                    backHistory               = new LinkedList<String>();\r
121     private final LinkedList<String>                    forwardHistory            = new LinkedList<String>();\r
122     private String                                      currentElement            = null;\r
123 \r
124     /**\r
125      * The Session used to access the graph. Received from outside of this\r
126      * class and therefore it is not disposed here, just used.\r
127      */\r
128     private final Session                               session;\r
129 \r
130     private final CopyOnWriteArrayList<HistoryListener> historyListeners          = new CopyOnWriteArrayList<HistoryListener>();\r
131 \r
132     protected Layer0                                    L0;\r
133 \r
134     protected boolean                                   disposed;\r
135 \r
136     class PageContentListener extends DisposableListener<String> {\r
137         int triggerCounter;\r
138         int updateCount;\r
139         AtomicReference<String> lastResult = new AtomicReference<String>();\r
140         @Override\r
141         public void execute(final String content) {\r
142             ++triggerCounter;\r
143             //System.out.println("LISTENER TRIGGERED: " + triggerCounter);\r
144             //System.out.println("LISTENER:\n" + content);\r
145             if (lastResult.getAndSet(content) == null) {\r
146                 if (!disposed) {\r
147                     getDisplay().asyncExec(new Runnable() {\r
148                         @Override\r
149                         public void run() {\r
150                             String content = lastResult.getAndSet(null);\r
151                             if (content == null)\r
152                                 return;\r
153 \r
154                             ++updateCount;\r
155                             //System.out.println("UPDATE " + updateCount);\r
156 \r
157                             if (!browser.isDisposed())\r
158                                 browser.setText(content);\r
159                             if (!updateTriggerCounter.isDisposed())\r
160                                 updateTriggerCounter.setText(updateCount + "/" + triggerCounter);\r
161                         }\r
162                     });\r
163                 }\r
164             }\r
165         }\r
166 \r
167         @Override\r
168         public void exception(Throwable t) {\r
169             Logger.defaultLogError(t);\r
170         }\r
171     }\r
172 \r
173     private PageContentListener pageContentListener;\r
174 \r
175     /**\r
176      * @param parent\r
177      * @param style\r
178      * @param session\r
179      * @param resource the initial resource to debug or <code>null</code> for\r
180      *        initially blank UI.\r
181      */\r
182     public VariableDebugger(Composite parent, int style, final Session session, String initialURI) {\r
183         super(parent, style);\r
184         Assert.isNotNull(session, "session is null");\r
185         this.session = session;\r
186         this.currentElement = initialURI;\r
187         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);\r
188 \r
189         initializeCSS();\r
190 \r
191         addDisposeListener(new DisposeListener() {\r
192             @Override\r
193             public void widgetDisposed(DisposeEvent e) {\r
194                 disposed = true;\r
195                 PageContentListener l = pageContentListener;\r
196                 if (l != null)\r
197                     l.dispose();\r
198             }\r
199         });\r
200     }\r
201 \r
202     public void defaultInitializeUI() {\r
203         setLayout(new GridLayout(4, false));\r
204         setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
205 \r
206         createDropLabel(this);\r
207         createResourceText(this);\r
208         createUpdateTriggerCounter(this);\r
209         Browser browser = createBrowser(this);\r
210         GridDataFactory.fillDefaults().span(4, 1).grab(true, true).applyTo(browser);\r
211     }\r
212 \r
213     protected void initializeCSS() {\r
214         // Extract default css to a temporary location if necessary.\r
215         try {\r
216             IPath absolutePath = PathUtils.getAbsolutePath(Activator.PLUGIN_ID, DEFAULT_DEBUGGER_CSS_PATH);\r
217             if (absolutePath != null) {\r
218                 cssPath = absolutePath.toFile().toURI().toString();\r
219             } else {\r
220                 File tempDir = FileUtils.getOrCreateTemporaryDirectory(false);\r
221                 File css = new File(tempDir, DEFAULT_DEBUGGER_CSS_FILE);\r
222                 if (!css.exists()) {\r
223                     URL url = FileLocator.find(Activator.getDefault().getBundle(), new Path(DEFAULT_DEBUGGER_CSS_PATH), null);\r
224                     if (url == null)\r
225                         throw new FileNotFoundException("Could not find '" + DEFAULT_DEBUGGER_CSS_PATH + "' in bundle '" + Activator.PLUGIN_ID + "'");\r
226                     cssPath = FileUtils.copyResource(url, css, true).toURI().toString();\r
227                 } else {\r
228                     cssPath = css.toURI().toString();\r
229                 }\r
230             }\r
231         } catch (IOException e) {\r
232             e.printStackTrace();\r
233             // CSS extraction failed, let's just live without it then.\r
234             ErrorLogger.defaultLogWarning(e);\r
235         }\r
236     }\r
237 \r
238     public Label createDropLabel(Composite parent) {\r
239         final Label label = new Label(parent, SWT.BORDER | SWT.FLAT);\r
240         label.setAlignment(SWT.CENTER);\r
241         label.setText("  Drag a resource or a variable here to examine it in this debugger!  ");\r
242         label.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));\r
243         GridData data = new GridData(SWT.LEFT, SWT.FILL, false, false);\r
244         label.setLayoutData(data);\r
245 \r
246         // Add resource id drop support to the drop-area.\r
247         DropTarget dropTarget = new DropTarget(label, DND.DROP_LINK | DND.DROP_COPY);\r
248         dropTarget.setTransfer(new Transfer[] { TextTransfer.getInstance(), ResourceReferenceTransfer.getInstance(), LocalObjectTransfer.getTransfer() });\r
249         dropTarget.addDropListener(new DropTargetAdapter() {\r
250             @Override\r
251             public void dragEnter(DropTargetEvent event) {\r
252                 event.detail = DND.DROP_LINK;\r
253                 label.setBackground((Color) resourceManager.get(green));\r
254                 return;\r
255             }\r
256             @Override\r
257             public void dragLeave(DropTargetEvent event) {\r
258                 label.setBackground(null);\r
259             }\r
260 \r
261             @Override\r
262             public void drop(DropTargetEvent event) {\r
263                 label.setBackground(null);\r
264                 try {\r
265                     String uri = parseUri(event);\r
266                     if (uri == null) {\r
267                         event.detail = DND.DROP_NONE;\r
268                         return;\r
269                     }\r
270                     changeLocation(uri);\r
271                 } catch (DatabaseException e) {\r
272                     Logger.defaultLogError(e);\r
273                 }\r
274             }\r
275 \r
276             private String parseUri(DropTargetEvent event) throws DatabaseException {\r
277                 Variable v = parseVariable(event);\r
278                 String uri = v != null ? session.sync(new VariableURI(v)) : null;\r
279                 if (uri == null) {\r
280                     Resource r = parseResource(event);\r
281                     uri = r != null ? session.sync(new PossibleURI(r)) : null;\r
282                 }\r
283                 return uri;\r
284             }\r
285 \r
286             private Variable parseVariable(DropTargetEvent event) {\r
287                 return ISelectionUtils.getSinglePossibleKey(event.data, SelectionHints.KEY_MAIN, Variable.class);\r
288             }\r
289 \r
290             private Resource parseResource(DropTargetEvent event) throws DatabaseException {\r
291                 ResourceArray[] ra = null;\r
292                 if (event.data instanceof String) {\r
293                     try {\r
294                         SerialisationSupport support = session.getService(SerialisationSupport.class);\r
295                         ra = ResourceTransferUtils.readStringTransferable(support, (String) event.data).toResourceArrayArray();\r
296                     } catch (IllegalArgumentException e) {\r
297                         e.printStackTrace();\r
298                     } catch (DatabaseException e) {\r
299                         e.printStackTrace();\r
300                     }\r
301                 } else {\r
302                     ra = ResourceAdaptionUtils.toResourceArrays(event.data);\r
303                 }\r
304                 if (ra != null && ra.length > 0)\r
305                     return ra[0].resources[ra[0].resources.length - 1];\r
306                 return null;\r
307             }\r
308         });\r
309 \r
310         return label;\r
311     }\r
312 \r
313     public void createResourceText(Composite parent) {\r
314         final Text text = new Text(parent, SWT.BORDER);\r
315         GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);\r
316         text.setLayoutData(data);\r
317 \r
318         Button button = new Button(parent, SWT.NONE);\r
319         button.setText("Lookup");\r
320         GridData data2 = new GridData(SWT.FILL, SWT.FILL, false, false);\r
321         button.setLayoutData(data2);\r
322 \r
323         button.addSelectionListener(new SelectionListener() {\r
324 \r
325             @Override\r
326             public void widgetDefaultSelected(SelectionEvent e) {\r
327                 widgetSelected(e);\r
328             }\r
329 \r
330             @Override\r
331             public void widgetSelected(SelectionEvent e) {\r
332 \r
333                 try {\r
334                     String uri = text.getText();\r
335                     // Make sure that URI is resolvable to Variable\r
336                     session.sync(new ResourceURIToVariable(uri));\r
337                     changeLocation(uri);\r
338                 } catch (DatabaseException e1) {\r
339                     Logger.defaultLogError(e1);\r
340                 }\r
341 \r
342             }\r
343 \r
344         });\r
345     }\r
346 \r
347     protected Text createUpdateTriggerCounter(Composite parent) {\r
348         Text label = new Text(parent, SWT.BORDER | SWT.FLAT);\r
349         label.setEditable(false);\r
350         label.setToolTipText("Amount of Screen/Listener Updates Received for Shown Variable");\r
351         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)\r
352         .grab(false, false).hint(32, SWT.DEFAULT).applyTo(label);\r
353         updateTriggerCounter = label;\r
354         return label;\r
355     }\r
356 \r
357     public Browser createBrowser(Composite parent) {\r
358         try {\r
359             browser = new Browser(parent, SWT.MOZILLA);\r
360         } catch (SWTError e) {\r
361             //System.out.println("Could not instantiate Browser: " + e.getMessage());\r
362             browser = new Browser(parent, SWT.NONE);\r
363         }\r
364         browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
365 \r
366         // Left/right arrows for back/forward\r
367         browser.addKeyListener(new KeyAdapter() {\r
368             @Override\r
369             public void keyReleased(KeyEvent e) {\r
370 //                System.out.println("key, char: " + e.keyCode + ", " + (int) e.character + " (" + e.character + ")");\r
371 //                if (e.keyCode == SWT.BS) {\r
372 //                    back();\r
373 //                }\r
374                 if ((e.stateMask & SWT.ALT) != 0) {\r
375                     if (e.keyCode == SWT.ARROW_RIGHT)\r
376                         forward();\r
377                     if (e.keyCode == SWT.ARROW_LEFT)\r
378                         back();\r
379                 }\r
380             }\r
381         });\r
382 \r
383         // Add listener for debugging functionality\r
384         browser.addLocationListener(new LocationAdapter() {\r
385             @Override\r
386             public void changing(LocationEvent event) {\r
387                 String location = event.location;\r
388                 if (location.startsWith("simantics:browser"))\r
389                     location = "about:" + location.substring(17);\r
390                 //System.out.println("changing: location=" + location);\r
391 \r
392                 // Do not follow links that are meant as actions that are\r
393                 // handled below.\r
394                 event.doit = false;\r
395                 if ("about:blank".equals(location)) {\r
396                     // Just changing to the same old blank url is ok since it\r
397                     // allows the browser to refresh itself.\r
398                     event.doit = true;\r
399                 }\r
400 \r
401                 if (location.startsWith("about:-link")) {\r
402                     String target = location.replace("about:-link", "");\r
403                     try {\r
404                         byte[] bytes = Base64.decode(target);\r
405                         String url = new String(bytes, utf8);\r
406                         if (url.equals(currentElement)) {\r
407                             event.doit = false;\r
408                             return;\r
409                         }\r
410                         changeLocation(url);\r
411                     } catch (IOException e) {\r
412                         ErrorLogger.defaultLogError(e);\r
413                     }\r
414                 } else if (location.startsWith("about:-remove")) {\r
415                 } else if (location.startsWith("about:-edit-value")) {\r
416                 }\r
417             }\r
418         });\r
419 \r
420         // Schedule a request that updates the browser content.\r
421         refreshBrowser();\r
422 \r
423         return browser;\r
424     }\r
425 \r
426     public void refreshBrowser() {\r
427         if (currentElement == null)\r
428             return;\r
429 \r
430         // Schedule a request that updates the browser content.\r
431         if (pageContentListener != null)\r
432             pageContentListener.dispose();\r
433         pageContentListener = new PageContentListener();\r
434         session.asyncRequest(new UnaryRead<String, String>(currentElement) {\r
435             @Override\r
436             public String perform(ReadGraph graph) throws DatabaseException {\r
437                 String content = calculateContent(graph, parameter);\r
438                 //System.out.println("HTML: " + content);\r
439                 return content;\r
440             }\r
441         }, pageContentListener);\r
442 \r
443     }\r
444 \r
445     public String getDebuggerLocation() {\r
446         return currentElement;\r
447     }\r
448 \r
449     public void changeLocation(String url) {\r
450         if (currentElement != null) {\r
451             backHistory.addLast(currentElement);\r
452         }\r
453         currentElement = url;\r
454         forwardHistory.clear();\r
455 \r
456         refreshBrowser();\r
457         fireHistoryChanged();\r
458     }\r
459 \r
460     public void addHistoryListener(HistoryListener l) {\r
461         historyListeners.add(l);\r
462     }\r
463 \r
464     public void removeHistoryListener(HistoryListener l) {\r
465         historyListeners.remove(l);\r
466     }\r
467 \r
468     private void fireHistoryChanged() {\r
469         for (HistoryListener l : historyListeners)\r
470             l.historyChanged();\r
471     }\r
472 \r
473     public boolean hasBackHistory() {\r
474         return backHistory.isEmpty();\r
475     }\r
476 \r
477     public boolean hasForwardHistory() {\r
478         return forwardHistory.isEmpty();\r
479     }\r
480 \r
481     public void back() {\r
482         if (backHistory.isEmpty())\r
483             return;\r
484 \r
485         forwardHistory.addFirst(currentElement);\r
486         currentElement = backHistory.removeLast();\r
487 \r
488         refreshBrowser();\r
489         fireHistoryChanged();\r
490     }\r
491 \r
492     public void forward() {\r
493         if (forwardHistory.isEmpty())\r
494             return;\r
495 \r
496         backHistory.addLast(currentElement);\r
497         currentElement = forwardHistory.removeFirst();\r
498 \r
499         refreshBrowser();\r
500         fireHistoryChanged();\r
501     }\r
502 \r
503     protected String toName(Object o) {\r
504         Class<?> clazz = o.getClass();\r
505         if (clazz.isArray()) {\r
506             int length = Array.getLength(o);\r
507             if (length > RESOURCE_NAME_MAX_LENGTH) {\r
508                 if (o instanceof byte[]) {\r
509                     byte[] arr = (byte[]) o;\r
510                     byte[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
511                     return truncated("byte", Arrays.toString(arr2), arr.length);\r
512                 } else if (o instanceof int[]) {\r
513                     int[] arr = (int[]) o;\r
514                     int[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
515                     return truncated("int", Arrays.toString(arr2), arr.length);\r
516                 } else if (o instanceof long[]) {\r
517                     long[] arr = (long[]) o;\r
518                     long[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
519                     return truncated("long", Arrays.toString(arr2), arr.length);\r
520                 } else if (o instanceof float[]) {\r
521                     float[] arr = (float[]) o;\r
522                     float[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
523                     return truncated("float", Arrays.toString(arr2), arr.length);\r
524                 } else if (o instanceof double[]) {\r
525                     double[] arr = (double[]) o;\r
526                     double[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
527                     return truncated("double", Arrays.toString(arr2), arr.length);\r
528                 } else if (o instanceof boolean[]) {\r
529                     boolean[] arr = (boolean[]) o;\r
530                     boolean[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
531                     return truncated("boolean", Arrays.toString(arr2), arr.length);\r
532                 } else if (o instanceof Object[]) {\r
533                     Object[] arr = (Object[]) o;\r
534                     Object[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
535                     return truncated("Object", Arrays.toString(arr2), arr.length);\r
536                 } else {\r
537                     return "Unknown big array " + o.getClass();\r
538                 }\r
539             } else {\r
540                 return o.getClass().getComponentType() + "[" + length + "] = " + ObjectUtils.toString(o);\r
541             }\r
542         }\r
543         return null;\r
544     }\r
545 \r
546     protected String truncated(String type, String string, int originalLength) {\r
547         return type + "[" + RESOURCE_NAME_MAX_LENGTH + "/" + originalLength + "] = " + string;\r
548     }\r
549 \r
550     protected String getVariableName(ReadGraph graph, Variable r) {\r
551         try {\r
552             return r.getName(graph);\r
553         } catch (Exception e) {\r
554             return e.getMessage();\r
555         }\r
556     }\r
557 \r
558     protected String getValue(ReadGraph graph, Variable base, Object o) throws DatabaseException {\r
559         Class<?> clazz = o.getClass();\r
560         if(o instanceof Connection) {\r
561             Connection c = (Connection)o;\r
562             ArrayList<String> result = new ArrayList<String>();\r
563             for(VariableConnectionPointDescriptor v : c.getConnectionPointDescriptors(graph, null)) {\r
564                 result.add(v.getRelativeRVI(graph, base));\r
565             }\r
566             return "c " + result.toString();\r
567         } else if (clazz.isArray()) {\r
568             if(int[].class == clazz) {\r
569                 return Arrays.toString((int[])o);\r
570             } else if(float[].class == clazz) {\r
571                 return Arrays.toString((float[])o);\r
572             } else if(double[].class == clazz) {\r
573                 return Arrays.toString((double[])o);\r
574             } else if(long[].class == clazz) {\r
575                 return Arrays.toString((long[])o);\r
576             } else if(byte[].class == clazz) {\r
577                 return Arrays.toString((byte[])o);\r
578             } else if(boolean[].class == clazz) {\r
579                 return Arrays.toString((boolean[])o);\r
580             } else if(char[].class == clazz) {\r
581                 return Arrays.toString((char[])o);\r
582             } else {\r
583                 return Arrays.toString((Object[])o);\r
584             }\r
585         }\r
586         return o.toString();\r
587     }\r
588     \r
589     protected String getValue(ReadGraph graph, Variable r) {\r
590         try {\r
591             Object value = r.getValue(graph);\r
592             if(value instanceof Resource) return getResourceRef(graph, (Resource)value);\r
593             else if (value instanceof Variable) return getVariableRef(graph, (Variable)value);\r
594             else return value != null ? getValue(graph, r, value) : "null";\r
595         } catch (Throwable e) {\r
596             try {\r
597                 Logger.defaultLogError("getValue " + r.getURI(graph), e);\r
598             } catch (DatabaseException e1) {\r
599                 Logger.defaultLogError(e1);\r
600             }\r
601             return e.getMessage();\r
602         }\r
603     }\r
604 \r
605     protected String getDatatype(ReadGraph graph, Variable r) {\r
606         try {\r
607             Datatype dt = r.getPossibleDatatype(graph);\r
608             return dt != null ? dt.toSingleLineString() : "undefined";\r
609         } catch (Exception e) {\r
610             return e.getMessage();\r
611         }\r
612     }\r
613 \r
614     private String getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {\r
615         return getVariableRef(graph, graph.adapt(r, Variable.class));\r
616     }\r
617 \r
618     private String getVariableRef(ReadGraph graph, Variable r) throws DatabaseException {\r
619         String ret = "<a href=\"simantics:browser-link" + getLinkString(graph, r) + "\">"\r
620         + getVariableName(graph, r)\r
621         + "</a>";\r
622 //        if (graph.isInstanceOf(r, L0.Literal)) {\r
623 //            ret += "&nbsp;<a class=\"edit-link\" href=\"simantics:browser-edit-value" + getLinkString(r) + "\">"\r
624 //            + "(edit value)"\r
625 //            + "</a>";\r
626 //        }\r
627         return ret;\r
628     }\r
629 \r
630     private String getLinkString(ReadGraph graph, Variable t) throws DatabaseException {\r
631         try {\r
632             String uri = t.getURI(graph);\r
633             //return uri;\r
634             String encoded = Base64.encode(uri.getBytes(utf8));\r
635             return encoded;\r
636         } catch (Exception e) {\r
637             Logger.defaultLogError(e);\r
638             return e.getMessage();\r
639         }\r
640     }\r
641 \r
642     private void updateProperty(StringBuilder content, ReadGraph graph, Variable property) throws DatabaseException {\r
643 //        try {\r
644 //            System.out.println("update property " + property.getURI(graph));\r
645 //        } catch (Exception e) {\r
646 //            e.printStackTrace();\r
647 //        }\r
648         content.append("<tr>");\r
649         content.append("<td>").append(getVariableRef(graph, property)).append("</td>");\r
650         content.append("<td>").append(getValue(graph, property)).append("</td>");\r
651         content.append("<td>").append(getDatatype(graph, property)).append("</td>");\r
652         content.append("</tr>");\r
653     }\r
654 \r
655     protected String getRVIString(ReadGraph graph, Variable var) throws DatabaseException {\r
656         \r
657         try {\r
658             return var.getRVI(graph).toString(graph);\r
659         } catch (Throwable e) {\r
660             return "No RVI";\r
661         }\r
662         \r
663     }\r
664     \r
665     protected synchronized String calculateContent(final ReadGraph graph, String... uris) throws DatabaseException {\r
666         \r
667         L0 = Layer0.getInstance(graph);\r
668 \r
669         StringBuilder content = new StringBuilder();\r
670 \r
671         // Generate HTML -page\r
672         content.append("<html><head>").append(getHead()).append("</head>\n");\r
673         content.append("<body>\n");\r
674         content.append("<div id=\"mainContent\">\n");\r
675         for (String uri : uris) {\r
676             //System.out.println("URI: " + uri);\r
677             Variable var = Variables.getPossibleVariable(graph, uri);\r
678             if (var == null)\r
679                 continue;\r
680 \r
681             String rviString = getRVIString(graph, var);\r
682             Object node = null;\r
683             if(var instanceof AbstractChildVariable) {\r
684                 VariableNode vn = ((AbstractChildVariable)var).node; \r
685                 if(vn != null) node = vn.node;\r
686             }\r
687             if(var instanceof AbstractPropertyVariable) {\r
688                 VariableNode vn = ((AbstractPropertyVariable)var).node;\r
689                 if(vn != null) node = vn.node;\r
690             }\r
691             \r
692             // Begin #top DIV\r
693             content.append("<div id=\"top\">\n");\r
694             content.append("<table class=\"top\">\n");\r
695             content.append("<tr><td class=\"top_key\">URI</td><td class=\"top_value\"><span id=\"uri\">").append(uri).append("</span></td></tr>\n");\r
696             content.append("<tr><td class=\"top_key\">RVI</td><td class=\"top_value\"><span id=\"uri\">").append(rviString).append("</span></td></tr>\n");\r
697             content.append("<tr><td class=\"top_key\">Class</td><td class=\"top_value\"><span id=\"class\">").append(var.getClass().getCanonicalName()).append("</span></td></tr>\n");\r
698             content.append("<tr><td class=\"top_key\">Solver node</td><td class=\"top_value\"><span id=\"class\">").append(node).append("</span></td></tr>\n");\r
699             content.append("</table>\n");\r
700             content.append("</div>\n");\r
701             // Close #top DIV\r
702 \r
703             // Content\r
704             TreeMap<String, Variable> map = new TreeMap<String, Variable>();\r
705             try {\r
706                 for(Variable child : var.getChildren(graph)) {\r
707                     String name = getVariableName(graph, child);\r
708                     map.put(name, child);\r
709                 }\r
710             } catch (DatabaseException e) {\r
711                 // This may happen if the Variable implementation is broken\r
712                 ErrorLogger.defaultLogError("Broken variable child retrieval implementation or serious modelling error encountered. See exception for details.", e);\r
713             }\r
714 \r
715             TreeMap<String, Variable> map2 = new TreeMap<String, Variable>();\r
716             try {\r
717                 for(Variable child : var.getProperties(graph)) {\r
718                     String name = getVariableName(graph, child);\r
719                     map2.put(name, child);\r
720                 }\r
721             } catch (DatabaseException e) {\r
722                 // This may happen if the Variable implementation is broken\r
723                 ErrorLogger.defaultLogError("Broken variable property retrieval implementation or serious modelling error encountered. See exception for details.", e);\r
724             }\r
725 \r
726             content.append("\n<div id=\"data\">\n");\r
727             content.append("<table>\n");\r
728 \r
729             content.append("<tr><th>Child</th></tr>");\r
730             for (Variable child : map.values()) {\r
731                 content.append("<tr><td>").append(getVariableRef(graph, child)).append("</td></tr>");\r
732             }\r
733 \r
734             content.append("<tr><th>Property</th><th>Value</th><th>Datatype</th></tr>");\r
735             for (Variable property : map2.values()) {\r
736                 updateProperty(content, graph, property);\r
737             }\r
738             // Close #data\r
739             content.append("</div>\n\n");\r
740         }\r
741 \r
742         // Close #mainContent\r
743         content.append("</div>\n");\r
744         content.append("</body></html>\n");\r
745 \r
746         // Update content\r
747         return content.toString();\r
748     }\r
749 \r
750     private String getHead() {\r
751         String result = "";\r
752         if (cssPath != null) {\r
753             result = "<link href=\"" + cssPath + "\" rel=\"stylesheet\" type=\"text/css\">";\r
754         }\r
755         return result;\r
756     }\r
757 \r
758 }\r