--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.debug.ui;\r
+\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.lang.reflect.Array;\r
+import java.net.URL;\r
+import java.nio.charset.Charset;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.LinkedList;\r
+import java.util.TreeMap;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.atomic.AtomicReference;\r
+\r
+import org.eclipse.core.runtime.Assert;\r
+import org.eclipse.core.runtime.FileLocator;\r
+import org.eclipse.core.runtime.IPath;\r
+import org.eclipse.core.runtime.Path;\r
+import org.eclipse.jface.layout.GridDataFactory;\r
+import org.eclipse.jface.resource.ColorDescriptor;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.SWTError;\r
+import org.eclipse.swt.browser.Browser;\r
+import org.eclipse.swt.browser.LocationAdapter;\r
+import org.eclipse.swt.browser.LocationEvent;\r
+import org.eclipse.swt.dnd.DND;\r
+import org.eclipse.swt.dnd.DropTarget;\r
+import org.eclipse.swt.dnd.DropTargetAdapter;\r
+import org.eclipse.swt.dnd.DropTargetEvent;\r
+import org.eclipse.swt.dnd.TextTransfer;\r
+import org.eclipse.swt.dnd.Transfer;\r
+import org.eclipse.swt.events.DisposeEvent;\r
+import org.eclipse.swt.events.DisposeListener;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.events.SelectionListener;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.graphics.RGB;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Button;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Label;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.util.ObjectUtils;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.common.ResourceArray;\r
+import org.simantics.db.common.procedure.adapter.DisposableListener;\r
+import org.simantics.db.common.request.UnaryRead;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.SelectionHints;\r
+import org.simantics.db.layer0.request.PossibleURI;\r
+import org.simantics.db.layer0.request.ResourceURIToVariable;\r
+import org.simantics.db.layer0.request.VariableURI;\r
+import org.simantics.db.layer0.variable.AbstractChildVariable;\r
+import org.simantics.db.layer0.variable.AbstractPropertyVariable;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.VariableNode;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.debug.ui.internal.Activator;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.structural2.variables.Connection;\r
+import org.simantics.structural2.variables.VariableConnectionPointDescriptor;\r
+import org.simantics.ui.dnd.LocalObjectTransfer;\r
+import org.simantics.ui.dnd.ResourceReferenceTransfer;\r
+import org.simantics.ui.dnd.ResourceTransferUtils;\r
+import org.simantics.ui.utils.ResourceAdaptionUtils;\r
+import org.simantics.utils.FileUtils;\r
+import org.simantics.utils.bytes.Base64;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+import org.simantics.utils.ui.ISelectionUtils;\r
+import org.simantics.utils.ui.PathUtils;\r
+\r
+\r
+/**\r
+ * @author Antti Villberg\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class VariableDebugger extends Composite {\r
+\r
+ public interface HistoryListener {\r
+ void historyChanged();\r
+ }\r
+\r
+ private final static String DEFAULT_DEBUGGER_CSS_FILE = "debugger.css";\r
+ private final static String DEFAULT_DEBUGGER_CSS_PATH = "css/" + DEFAULT_DEBUGGER_CSS_FILE;\r
+\r
+ private static int RESOURCE_NAME_MAX_LENGTH = 1000;\r
+\r
+ private final Charset utf8 = Charset.forName("UTF-8");\r
+\r
+ private final LocalResourceManager resourceManager;\r
+\r
+ private String cssPath;\r
+\r
+ private Text updateTriggerCounter; \r
+ private Browser browser;\r
+ private final ColorDescriptor green = ColorDescriptor.createFrom(new RGB(0x57, 0xbc, 0x95));\r
+\r
+ private final LinkedList<String> backHistory = new LinkedList<String>();\r
+ private final LinkedList<String> forwardHistory = new LinkedList<String>();\r
+ private String currentElement = null;\r
+\r
+ /**\r
+ * The Session used to access the graph. Received from outside of this\r
+ * class and therefore it is not disposed here, just used.\r
+ */\r
+ private final Session session;\r
+\r
+ private final CopyOnWriteArrayList<HistoryListener> historyListeners = new CopyOnWriteArrayList<HistoryListener>();\r
+\r
+ protected Layer0 L0;\r
+\r
+ protected boolean disposed;\r
+\r
+ class PageContentListener extends DisposableListener<String> {\r
+ int triggerCounter;\r
+ int updateCount;\r
+ AtomicReference<String> lastResult = new AtomicReference<String>();\r
+ @Override\r
+ public void execute(final String content) {\r
+ ++triggerCounter;\r
+ //System.out.println("LISTENER TRIGGERED: " + triggerCounter);\r
+ //System.out.println("LISTENER:\n" + content);\r
+ if (lastResult.getAndSet(content) == null) {\r
+ if (!disposed) {\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ String content = lastResult.getAndSet(null);\r
+ if (content == null)\r
+ return;\r
+\r
+ ++updateCount;\r
+ //System.out.println("UPDATE " + updateCount);\r
+\r
+ if (!browser.isDisposed())\r
+ browser.setText(content);\r
+ if (!updateTriggerCounter.isDisposed())\r
+ updateTriggerCounter.setText(updateCount + "/" + triggerCounter);\r
+ }\r
+ });\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void exception(Throwable t) {\r
+ Logger.defaultLogError(t);\r
+ }\r
+ }\r
+\r
+ private PageContentListener pageContentListener;\r
+\r
+ /**\r
+ * @param parent\r
+ * @param style\r
+ * @param session\r
+ * @param resource the initial resource to debug or <code>null</code> for\r
+ * initially blank UI.\r
+ */\r
+ public VariableDebugger(Composite parent, int style, final Session session, String initialURI) {\r
+ super(parent, style);\r
+ Assert.isNotNull(session, "session is null");\r
+ this.session = session;\r
+ this.currentElement = initialURI;\r
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);\r
+\r
+ initializeCSS();\r
+\r
+ addDisposeListener(new DisposeListener() {\r
+ @Override\r
+ public void widgetDisposed(DisposeEvent e) {\r
+ disposed = true;\r
+ PageContentListener l = pageContentListener;\r
+ if (l != null)\r
+ l.dispose();\r
+ }\r
+ });\r
+ }\r
+\r
+ public void defaultInitializeUI() {\r
+ setLayout(new GridLayout(4, false));\r
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
+\r
+ createDropLabel(this);\r
+ createResourceText(this);\r
+ createUpdateTriggerCounter(this);\r
+ Browser browser = createBrowser(this);\r
+ GridDataFactory.fillDefaults().span(4, 1).grab(true, true).applyTo(browser);\r
+ }\r
+\r
+ protected void initializeCSS() {\r
+ // Extract default css to a temporary location if necessary.\r
+ try {\r
+ IPath absolutePath = PathUtils.getAbsolutePath(Activator.PLUGIN_ID, DEFAULT_DEBUGGER_CSS_PATH);\r
+ if (absolutePath != null) {\r
+ cssPath = absolutePath.toFile().toURI().toString();\r
+ } else {\r
+ File tempDir = FileUtils.getOrCreateTemporaryDirectory(false);\r
+ File css = new File(tempDir, DEFAULT_DEBUGGER_CSS_FILE);\r
+ if (!css.exists()) {\r
+ URL url = FileLocator.find(Activator.getDefault().getBundle(), new Path(DEFAULT_DEBUGGER_CSS_PATH), null);\r
+ if (url == null)\r
+ throw new FileNotFoundException("Could not find '" + DEFAULT_DEBUGGER_CSS_PATH + "' in bundle '" + Activator.PLUGIN_ID + "'");\r
+ cssPath = FileUtils.copyResource(url, css, true).toURI().toString();\r
+ } else {\r
+ cssPath = css.toURI().toString();\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ // CSS extraction failed, let's just live without it then.\r
+ ErrorLogger.defaultLogWarning(e);\r
+ }\r
+ }\r
+\r
+ public Label createDropLabel(Composite parent) {\r
+ final Label label = new Label(parent, SWT.BORDER | SWT.FLAT);\r
+ label.setAlignment(SWT.CENTER);\r
+ label.setText(" Drag a resource or a variable here to examine it in this debugger! ");\r
+ label.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));\r
+ GridData data = new GridData(SWT.LEFT, SWT.FILL, false, false);\r
+ label.setLayoutData(data);\r
+\r
+ // Add resource id drop support to the drop-area.\r
+ DropTarget dropTarget = new DropTarget(label, DND.DROP_LINK | DND.DROP_COPY);\r
+ dropTarget.setTransfer(new Transfer[] { TextTransfer.getInstance(), ResourceReferenceTransfer.getInstance(), LocalObjectTransfer.getTransfer() });\r
+ dropTarget.addDropListener(new DropTargetAdapter() {\r
+ @Override\r
+ public void dragEnter(DropTargetEvent event) {\r
+ event.detail = DND.DROP_LINK;\r
+ label.setBackground((Color) resourceManager.get(green));\r
+ return;\r
+ }\r
+ @Override\r
+ public void dragLeave(DropTargetEvent event) {\r
+ label.setBackground(null);\r
+ }\r
+\r
+ @Override\r
+ public void drop(DropTargetEvent event) {\r
+ label.setBackground(null);\r
+ try {\r
+ String uri = parseUri(event);\r
+ if (uri == null) {\r
+ event.detail = DND.DROP_NONE;\r
+ return;\r
+ }\r
+ changeLocation(uri);\r
+ } catch (DatabaseException e) {\r
+ Logger.defaultLogError(e);\r
+ }\r
+ }\r
+\r
+ private String parseUri(DropTargetEvent event) throws DatabaseException {\r
+ Variable v = parseVariable(event);\r
+ String uri = v != null ? session.sync(new VariableURI(v)) : null;\r
+ if (uri == null) {\r
+ Resource r = parseResource(event);\r
+ uri = r != null ? session.sync(new PossibleURI(r)) : null;\r
+ }\r
+ return uri;\r
+ }\r
+\r
+ private Variable parseVariable(DropTargetEvent event) {\r
+ return ISelectionUtils.getSinglePossibleKey(event.data, SelectionHints.KEY_MAIN, Variable.class);\r
+ }\r
+\r
+ private Resource parseResource(DropTargetEvent event) throws DatabaseException {\r
+ ResourceArray[] ra = null;\r
+ if (event.data instanceof String) {\r
+ try {\r
+ SerialisationSupport support = session.getService(SerialisationSupport.class);\r
+ ra = ResourceTransferUtils.readStringTransferable(support, (String) event.data).toResourceArrayArray();\r
+ } catch (IllegalArgumentException e) {\r
+ e.printStackTrace();\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ } else {\r
+ ra = ResourceAdaptionUtils.toResourceArrays(event.data);\r
+ }\r
+ if (ra != null && ra.length > 0)\r
+ return ra[0].resources[ra[0].resources.length - 1];\r
+ return null;\r
+ }\r
+ });\r
+\r
+ return label;\r
+ }\r
+\r
+ public void createResourceText(Composite parent) {\r
+ final Text text = new Text(parent, SWT.BORDER);\r
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);\r
+ text.setLayoutData(data);\r
+\r
+ Button button = new Button(parent, SWT.NONE);\r
+ button.setText("Lookup");\r
+ GridData data2 = new GridData(SWT.FILL, SWT.FILL, false, false);\r
+ button.setLayoutData(data2);\r
+\r
+ button.addSelectionListener(new SelectionListener() {\r
+\r
+ @Override\r
+ public void widgetDefaultSelected(SelectionEvent e) {\r
+ widgetSelected(e);\r
+ }\r
+\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+\r
+ try {\r
+ String uri = text.getText();\r
+ // Make sure that URI is resolvable to Variable\r
+ session.sync(new ResourceURIToVariable(uri));\r
+ changeLocation(uri);\r
+ } catch (DatabaseException e1) {\r
+ Logger.defaultLogError(e1);\r
+ }\r
+\r
+ }\r
+\r
+ });\r
+ }\r
+\r
+ protected Text createUpdateTriggerCounter(Composite parent) {\r
+ Text label = new Text(parent, SWT.BORDER | SWT.FLAT);\r
+ label.setEditable(false);\r
+ label.setToolTipText("Amount of Screen/Listener Updates Received for Shown Variable");\r
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)\r
+ .grab(false, false).hint(32, SWT.DEFAULT).applyTo(label);\r
+ updateTriggerCounter = label;\r
+ return label;\r
+ }\r
+\r
+ public Browser createBrowser(Composite parent) {\r
+ try {\r
+ browser = new Browser(parent, SWT.MOZILLA);\r
+ } catch (SWTError e) {\r
+ //System.out.println("Could not instantiate Browser: " + e.getMessage());\r
+ browser = new Browser(parent, SWT.NONE);\r
+ }\r
+ browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
+\r
+ // Left/right arrows for back/forward\r
+ browser.addKeyListener(new KeyAdapter() {\r
+ @Override\r
+ public void keyReleased(KeyEvent e) {\r
+// System.out.println("key, char: " + e.keyCode + ", " + (int) e.character + " (" + e.character + ")");\r
+// if (e.keyCode == SWT.BS) {\r
+// back();\r
+// }\r
+ if ((e.stateMask & SWT.ALT) != 0) {\r
+ if (e.keyCode == SWT.ARROW_RIGHT)\r
+ forward();\r
+ if (e.keyCode == SWT.ARROW_LEFT)\r
+ back();\r
+ }\r
+ }\r
+ });\r
+\r
+ // Add listener for debugging functionality\r
+ browser.addLocationListener(new LocationAdapter() {\r
+ @Override\r
+ public void changing(LocationEvent event) {\r
+ String location = event.location;\r
+ if (location.startsWith("simantics:browser"))\r
+ location = "about:" + location.substring(17);\r
+ //System.out.println("changing: location=" + location);\r
+\r
+ // Do not follow links that are meant as actions that are\r
+ // handled below.\r
+ event.doit = false;\r
+ if ("about:blank".equals(location)) {\r
+ // Just changing to the same old blank url is ok since it\r
+ // allows the browser to refresh itself.\r
+ event.doit = true;\r
+ }\r
+\r
+ if (location.startsWith("about:-link")) {\r
+ String target = location.replace("about:-link", "");\r
+ try {\r
+ byte[] bytes = Base64.decode(target);\r
+ String url = new String(bytes, utf8);\r
+ if (url.equals(currentElement)) {\r
+ event.doit = false;\r
+ return;\r
+ }\r
+ changeLocation(url);\r
+ } catch (IOException e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ }\r
+ } else if (location.startsWith("about:-remove")) {\r
+ } else if (location.startsWith("about:-edit-value")) {\r
+ }\r
+ }\r
+ });\r
+\r
+ // Schedule a request that updates the browser content.\r
+ refreshBrowser();\r
+\r
+ return browser;\r
+ }\r
+\r
+ public void refreshBrowser() {\r
+ if (currentElement == null)\r
+ return;\r
+\r
+ // Schedule a request that updates the browser content.\r
+ if (pageContentListener != null)\r
+ pageContentListener.dispose();\r
+ pageContentListener = new PageContentListener();\r
+ session.asyncRequest(new UnaryRead<String, String>(currentElement) {\r
+ @Override\r
+ public String perform(ReadGraph graph) throws DatabaseException {\r
+ String content = calculateContent(graph, parameter);\r
+ //System.out.println("HTML: " + content);\r
+ return content;\r
+ }\r
+ }, pageContentListener);\r
+\r
+ }\r
+\r
+ public String getDebuggerLocation() {\r
+ return currentElement;\r
+ }\r
+\r
+ public void changeLocation(String url) {\r
+ if (currentElement != null) {\r
+ backHistory.addLast(currentElement);\r
+ }\r
+ currentElement = url;\r
+ forwardHistory.clear();\r
+\r
+ refreshBrowser();\r
+ fireHistoryChanged();\r
+ }\r
+\r
+ public void addHistoryListener(HistoryListener l) {\r
+ historyListeners.add(l);\r
+ }\r
+\r
+ public void removeHistoryListener(HistoryListener l) {\r
+ historyListeners.remove(l);\r
+ }\r
+\r
+ private void fireHistoryChanged() {\r
+ for (HistoryListener l : historyListeners)\r
+ l.historyChanged();\r
+ }\r
+\r
+ public boolean hasBackHistory() {\r
+ return backHistory.isEmpty();\r
+ }\r
+\r
+ public boolean hasForwardHistory() {\r
+ return forwardHistory.isEmpty();\r
+ }\r
+\r
+ public void back() {\r
+ if (backHistory.isEmpty())\r
+ return;\r
+\r
+ forwardHistory.addFirst(currentElement);\r
+ currentElement = backHistory.removeLast();\r
+\r
+ refreshBrowser();\r
+ fireHistoryChanged();\r
+ }\r
+\r
+ public void forward() {\r
+ if (forwardHistory.isEmpty())\r
+ return;\r
+\r
+ backHistory.addLast(currentElement);\r
+ currentElement = forwardHistory.removeFirst();\r
+\r
+ refreshBrowser();\r
+ fireHistoryChanged();\r
+ }\r
+\r
+ protected String toName(Object o) {\r
+ Class<?> clazz = o.getClass();\r
+ if (clazz.isArray()) {\r
+ int length = Array.getLength(o);\r
+ if (length > RESOURCE_NAME_MAX_LENGTH) {\r
+ if (o instanceof byte[]) {\r
+ byte[] arr = (byte[]) o;\r
+ byte[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("byte", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof int[]) {\r
+ int[] arr = (int[]) o;\r
+ int[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("int", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof long[]) {\r
+ long[] arr = (long[]) o;\r
+ long[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("long", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof float[]) {\r
+ float[] arr = (float[]) o;\r
+ float[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("float", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof double[]) {\r
+ double[] arr = (double[]) o;\r
+ double[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("double", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof boolean[]) {\r
+ boolean[] arr = (boolean[]) o;\r
+ boolean[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("boolean", Arrays.toString(arr2), arr.length);\r
+ } else if (o instanceof Object[]) {\r
+ Object[] arr = (Object[]) o;\r
+ Object[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);\r
+ return truncated("Object", Arrays.toString(arr2), arr.length);\r
+ } else {\r
+ return "Unknown big array " + o.getClass();\r
+ }\r
+ } else {\r
+ return o.getClass().getComponentType() + "[" + length + "] = " + ObjectUtils.toString(o);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ protected String truncated(String type, String string, int originalLength) {\r
+ return type + "[" + RESOURCE_NAME_MAX_LENGTH + "/" + originalLength + "] = " + string;\r
+ }\r
+\r
+ protected String getVariableName(ReadGraph graph, Variable r) {\r
+ try {\r
+ return r.getName(graph);\r
+ } catch (Exception e) {\r
+ return e.getMessage();\r
+ }\r
+ }\r
+\r
+ protected String getValue(ReadGraph graph, Variable base, Object o) throws DatabaseException {\r
+ Class<?> clazz = o.getClass();\r
+ if(o instanceof Connection) {\r
+ Connection c = (Connection)o;\r
+ ArrayList<String> result = new ArrayList<String>();\r
+ for(VariableConnectionPointDescriptor v : c.getConnectionPointDescriptors(graph, null)) {\r
+ result.add(v.getRelativeRVI(graph, base));\r
+ }\r
+ return "c " + result.toString();\r
+ } else if (clazz.isArray()) {\r
+ if(int[].class == clazz) {\r
+ return Arrays.toString((int[])o);\r
+ } else if(float[].class == clazz) {\r
+ return Arrays.toString((float[])o);\r
+ } else if(double[].class == clazz) {\r
+ return Arrays.toString((double[])o);\r
+ } else if(long[].class == clazz) {\r
+ return Arrays.toString((long[])o);\r
+ } else if(byte[].class == clazz) {\r
+ return Arrays.toString((byte[])o);\r
+ } else if(boolean[].class == clazz) {\r
+ return Arrays.toString((boolean[])o);\r
+ } else if(char[].class == clazz) {\r
+ return Arrays.toString((char[])o);\r
+ } else {\r
+ return Arrays.toString((Object[])o);\r
+ }\r
+ }\r
+ return o.toString();\r
+ }\r
+ \r
+ protected String getValue(ReadGraph graph, Variable r) {\r
+ try {\r
+ Object value = r.getValue(graph);\r
+ if(value instanceof Resource) return getResourceRef(graph, (Resource)value);\r
+ else if (value instanceof Variable) return getVariableRef(graph, (Variable)value);\r
+ else return value != null ? getValue(graph, r, value) : "null";\r
+ } catch (Throwable e) {\r
+ try {\r
+ Logger.defaultLogError("getValue " + r.getURI(graph), e);\r
+ } catch (DatabaseException e1) {\r
+ Logger.defaultLogError(e1);\r
+ }\r
+ return e.getMessage();\r
+ }\r
+ }\r
+\r
+ protected String getDatatype(ReadGraph graph, Variable r) {\r
+ try {\r
+ Datatype dt = r.getPossibleDatatype(graph);\r
+ return dt != null ? dt.toSingleLineString() : "undefined";\r
+ } catch (Exception e) {\r
+ return e.getMessage();\r
+ }\r
+ }\r
+\r
+ private String getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {\r
+ return getVariableRef(graph, graph.adapt(r, Variable.class));\r
+ }\r
+\r
+ private String getVariableRef(ReadGraph graph, Variable r) throws DatabaseException {\r
+ String ret = "<a href=\"simantics:browser-link" + getLinkString(graph, r) + "\">"\r
+ + getVariableName(graph, r)\r
+ + "</a>";\r
+// if (graph.isInstanceOf(r, L0.Literal)) {\r
+// ret += " <a class=\"edit-link\" href=\"simantics:browser-edit-value" + getLinkString(r) + "\">"\r
+// + "(edit value)"\r
+// + "</a>";\r
+// }\r
+ return ret;\r
+ }\r
+\r
+ private String getLinkString(ReadGraph graph, Variable t) throws DatabaseException {\r
+ try {\r
+ String uri = t.getURI(graph);\r
+ //return uri;\r
+ String encoded = Base64.encode(uri.getBytes(utf8));\r
+ return encoded;\r
+ } catch (Exception e) {\r
+ Logger.defaultLogError(e);\r
+ return e.getMessage();\r
+ }\r
+ }\r
+\r
+ private void updateProperty(StringBuilder content, ReadGraph graph, Variable property) throws DatabaseException {\r
+// try {\r
+// System.out.println("update property " + property.getURI(graph));\r
+// } catch (Exception e) {\r
+// e.printStackTrace();\r
+// }\r
+ content.append("<tr>");\r
+ content.append("<td>").append(getVariableRef(graph, property)).append("</td>");\r
+ content.append("<td>").append(getValue(graph, property)).append("</td>");\r
+ content.append("<td>").append(getDatatype(graph, property)).append("</td>");\r
+ content.append("</tr>");\r
+ }\r
+\r
+ protected String getRVIString(ReadGraph graph, Variable var) throws DatabaseException {\r
+ \r
+ try {\r
+ return var.getRVI(graph).toString(graph);\r
+ } catch (Throwable e) {\r
+ return "No RVI";\r
+ }\r
+ \r
+ }\r
+ \r
+ protected synchronized String calculateContent(final ReadGraph graph, String... uris) throws DatabaseException {\r
+ \r
+ L0 = Layer0.getInstance(graph);\r
+\r
+ StringBuilder content = new StringBuilder();\r
+\r
+ // Generate HTML -page\r
+ content.append("<html><head>").append(getHead()).append("</head>\n");\r
+ content.append("<body>\n");\r
+ content.append("<div id=\"mainContent\">\n");\r
+ for (String uri : uris) {\r
+ //System.out.println("URI: " + uri);\r
+ Variable var = Variables.getPossibleVariable(graph, uri);\r
+ if (var == null)\r
+ continue;\r
+\r
+ String rviString = getRVIString(graph, var);\r
+ Object node = null;\r
+ if(var instanceof AbstractChildVariable) {\r
+ VariableNode vn = ((AbstractChildVariable)var).node; \r
+ if(vn != null) node = vn.node;\r
+ }\r
+ if(var instanceof AbstractPropertyVariable) {\r
+ VariableNode vn = ((AbstractPropertyVariable)var).node;\r
+ if(vn != null) node = vn.node;\r
+ }\r
+ \r
+ // Begin #top DIV\r
+ content.append("<div id=\"top\">\n");\r
+ content.append("<table class=\"top\">\n");\r
+ content.append("<tr><td class=\"top_key\">URI</td><td class=\"top_value\"><span id=\"uri\">").append(uri).append("</span></td></tr>\n");\r
+ content.append("<tr><td class=\"top_key\">RVI</td><td class=\"top_value\"><span id=\"uri\">").append(rviString).append("</span></td></tr>\n");\r
+ 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
+ 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
+ content.append("</table>\n");\r
+ content.append("</div>\n");\r
+ // Close #top DIV\r
+\r
+ // Content\r
+ TreeMap<String, Variable> map = new TreeMap<String, Variable>();\r
+ try {\r
+ for(Variable child : var.getChildren(graph)) {\r
+ String name = getVariableName(graph, child);\r
+ map.put(name, child);\r
+ }\r
+ } catch (DatabaseException e) {\r
+ // This may happen if the Variable implementation is broken\r
+ ErrorLogger.defaultLogError("Broken variable child retrieval implementation or serious modelling error encountered. See exception for details.", e);\r
+ }\r
+\r
+ TreeMap<String, Variable> map2 = new TreeMap<String, Variable>();\r
+ try {\r
+ for(Variable child : var.getProperties(graph)) {\r
+ String name = getVariableName(graph, child);\r
+ map2.put(name, child);\r
+ }\r
+ } catch (DatabaseException e) {\r
+ // This may happen if the Variable implementation is broken\r
+ ErrorLogger.defaultLogError("Broken variable property retrieval implementation or serious modelling error encountered. See exception for details.", e);\r
+ }\r
+\r
+ content.append("\n<div id=\"data\">\n");\r
+ content.append("<table>\n");\r
+\r
+ content.append("<tr><th>Child</th></tr>");\r
+ for (Variable child : map.values()) {\r
+ content.append("<tr><td>").append(getVariableRef(graph, child)).append("</td></tr>");\r
+ }\r
+\r
+ content.append("<tr><th>Property</th><th>Value</th><th>Datatype</th></tr>");\r
+ for (Variable property : map2.values()) {\r
+ updateProperty(content, graph, property);\r
+ }\r
+ // Close #data\r
+ content.append("</div>\n\n");\r
+ }\r
+\r
+ // Close #mainContent\r
+ content.append("</div>\n");\r
+ content.append("</body></html>\n");\r
+\r
+ // Update content\r
+ return content.toString();\r
+ }\r
+\r
+ private String getHead() {\r
+ String result = "";\r
+ if (cssPath != null) {\r
+ result = "<link href=\"" + cssPath + "\" rel=\"stylesheet\" type=\"text/css\">";\r
+ }\r
+ return result;\r
+ }\r
+\r
+}\r