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