--- /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.URI;\r
+import java.net.URISyntaxException;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.UUID;\r
+import java.util.concurrent.CopyOnWriteArrayList;\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.action.IStatusLineManager;\r
+import org.eclipse.jface.dialogs.Dialog;\r
+import org.eclipse.jface.dialogs.IDialogConstants;\r
+import org.eclipse.jface.dialogs.IDialogSettings;\r
+import org.eclipse.jface.dialogs.IInputValidator;\r
+import org.eclipse.jface.dialogs.InputDialog;\r
+import org.eclipse.jface.dialogs.MessageDialog;\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.FocusEvent;\r
+import org.eclipse.swt.events.FocusListener;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.ModifyEvent;\r
+import org.eclipse.swt.events.ModifyListener;\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.Point;\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.eclipse.ui.IWorkbenchSite;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Databoard;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.util.ObjectUtils;\r
+import org.simantics.db.AsyncRequestProcessor;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.VirtualGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.ResourceArray;\r
+import org.simantics.db.common.procedure.adapter.ProcedureAdapter;\r
+import org.simantics.db.common.request.Queries;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.uri.ResourceToPossibleURI;\r
+import org.simantics.db.common.utils.ListUtils;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.event.ChangeEvent;\r
+import org.simantics.db.event.ChangeListener;\r
+import org.simantics.db.exception.AdaptionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ResourceNotFoundException;\r
+import org.simantics.db.exception.ValidationException;\r
+import org.simantics.db.layer0.adapter.StringModifier;\r
+import org.simantics.db.layer0.variable.RVI;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.db.service.ClusteringSupport;\r
+import org.simantics.db.service.GraphChangeListenerSupport;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.db.service.VirtualGraphSupport;\r
+import org.simantics.db.service.XSupport;\r
+import org.simantics.debug.ui.internal.Activator;\r
+import org.simantics.debug.ui.internal.DebugUtils;\r
+import org.simantics.debug.ui.internal.HashMultiMap;\r
+import org.simantics.layer0.Layer0;\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.Container;\r
+import org.simantics.utils.FileUtils;\r
+import org.simantics.utils.datastructures.BijectionMap;\r
+import org.simantics.utils.datastructures.Callback;\r
+import org.simantics.utils.strings.AlphanumComparator;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+import org.simantics.utils.ui.PathUtils;\r
+import org.simantics.utils.ui.workbench.WorkbenchUtils;\r
+\r
+\r
+public class GraphDebugger extends Composite {\r
+\r
+ public interface HistoryListener {\r
+ void historyChanged();\r
+ }\r
+\r
+ private static final String STATEMENT_PART_SEPARATOR = ",";\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
+ private static int RESOURCE_VALUE_MAX_SIZE = 16384;\r
+\r
+ private final LocalResourceManager resourceManager;\r
+\r
+ private String cssPath;\r
+\r
+ private Browser browser;\r
+ private final ColorDescriptor green = ColorDescriptor.createFrom(new RGB(0x57, 0xbc, 0x95));\r
+\r
+ private final boolean displayClusters = true;\r
+\r
+ private final BijectionMap<String, Resource> links = new BijectionMap<String, Resource>();\r
+ private final LinkedList<Resource> backHistory = new LinkedList<Resource>();\r
+ private final LinkedList<Resource> forwardHistory = new LinkedList<Resource>();\r
+ private Resource 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
+ private final AsyncRequestProcessor updater;\r
+\r
+ protected Layer0 L0;\r
+\r
+ protected IWorkbenchSite site;\r
+\r
+ private final ChangeListener changeListener = new ChangeListener() {\r
+ @Override\r
+ public void graphChanged(ChangeEvent e) {\r
+ // This makes sure that the transaction for updating this\r
+ // GraphDebugger get executed in a serialized fashion.\r
+ updater.asyncRequest(new ReadRequest() {\r
+\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ updateContent(graph, currentElement);\r
+ }\r
+\r
+ });\r
+\r
+ }\r
+ };\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
+ * @param site the workbench site that contains this debugger, for workbench\r
+ * service access\r
+ */\r
+ public GraphDebugger(Composite parent, int style, final Session session, Resource resource, IWorkbenchSite site) {\r
+ this(parent, style, session, resource);\r
+ this.site = site;\r
+ }\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 GraphDebugger(Composite parent, int style, final Session session, Resource resource) {\r
+ super(parent, style);\r
+ Assert.isNotNull(session, "session is null");\r
+ this.session = session;\r
+ this.currentElement = resource;\r
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);\r
+\r
+ updater = session;//.getService(MergingGraphRequestProcessor.class);\r
+\r
+ initializeCSS();\r
+\r
+ addDisposeListener(new DisposeListener() {\r
+ @Override\r
+ public void widgetDisposed(DisposeEvent e) {\r
+ GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);\r
+ support.removeListener(changeListener);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * When given to setStatus, indicates that the message shouldn't be touched\r
+ * since <code>null</code> has a different meaning.\r
+ */\r
+ private static final String DONT_TOUCH = "DONT_TOUCH";\r
+\r
+ protected void setStatus(String message, String error) {\r
+ IStatusLineManager status = WorkbenchUtils.getStatusLine(site);\r
+ if (status != null) {\r
+ if (message != DONT_TOUCH)\r
+ status.setMessage(message);\r
+ if (error != DONT_TOUCH)\r
+ status.setErrorMessage(error);\r
+ }\r
+ }\r
+\r
+ public void defaultInitializeUI() {\r
+ setLayout(new GridLayout(2, false));\r
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
+\r
+ createResourceText(this);\r
+ createDropLabel(this);\r
+ createBrowser(this);\r
+\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
+ // CSS extraction failed, let's just live without it then.\r
+ ErrorLogger.defaultLogWarning(e);\r
+ }\r
+ }\r
+\r
+ private static final String PROMPT_TEXT = "Enter resource ID (RID) or URI";\r
+\r
+ public void createResourceText(final Composite parent) {\r
+ final Text text = new Text(parent, SWT.BORDER);\r
+ text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));\r
+ text.setText(PROMPT_TEXT);\r
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(text);\r
+ \r
+ text.addFocusListener(new FocusListener() { \r
+ @Override\r
+ public void focusLost(FocusEvent e) {\r
+ if (text.getText().trim().equals("")) {\r
+ text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));\r
+ text.setText(PROMPT_TEXT);\r
+ }\r
+ }\r
+ @Override\r
+ public void focusGained(FocusEvent e) {\r
+ if (text.getText().trim().equals(PROMPT_TEXT)) {\r
+ text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_BLACK));\r
+ text.setText("");\r
+ }\r
+ text.selectAll();\r
+ }\r
+ });\r
+ text.addKeyListener(new KeyAdapter() {\r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ if (e.keyCode == SWT.CR) {\r
+ String input = text.getText();\r
+ setLookupInput(input);\r
+ }\r
+ }\r
+ });\r
+\r
+ final Button button = new Button(parent, SWT.FLAT);\r
+ button.setText("&Lookup");\r
+ button.setEnabled(false);\r
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(button);\r
+\r
+ text.addKeyListener(new KeyAdapter() {\r
+ \r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ if (e.keyCode == 13) {\r
+ String input = text.getText();\r
+ setLookupInput(input);\r
+ button.setFocus();\r
+ }\r
+ }\r
+ });\r
+ \r
+ button.addSelectionListener(new SelectionListener() {\r
+ @Override\r
+ public void widgetDefaultSelected(SelectionEvent e) {\r
+ widgetSelected(e);\r
+ }\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ String input = text.getText();\r
+ setLookupInput(input);\r
+ }\r
+ });\r
+ \r
+ text.addModifyListener(new ModifyListener() {\r
+ @Override\r
+ public void modifyText(ModifyEvent e) {\r
+ String input = text.getText().trim();\r
+ if (!input.equals(PROMPT_TEXT) && !input.equals(""))\r
+ button.setEnabled(true);\r
+ else\r
+ button.setEnabled(false);\r
+ }\r
+ });\r
+ }\r
+\r
+ public void setLookupInput(String input) {\r
+ // There's no harm in trimming out spaces from both ends of the input.\r
+ input = input.trim();\r
+\r
+ SerialisationSupport support = session.getService(SerialisationSupport.class);\r
+ if (input.startsWith("$")) {\r
+ try {\r
+ Resource r = support.getResource(Long.parseLong(input.substring(1)));\r
+ changeLocation(r);\r
+ } catch (NumberFormatException e1) {\r
+ // Ignore, may happen for crap input\r
+ setStatus(DONT_TOUCH, "Invalid '$'-prefixed input, expected resource ID");\r
+ } catch (Exception e1) {\r
+ ErrorLogger.defaultLogError(e1);\r
+ setStatus(DONT_TOUCH, "Resource ID lookup failed. See Error Log.");\r
+ }\r
+ return;\r
+ }\r
+\r
+ String[] parts = input.split("-");\r
+\r
+ if (parts.length == 1) {\r
+ try {\r
+ int resourceKey = Integer.parseInt(parts[0].trim());\r
+ Resource r = support.getResource(resourceKey);\r
+ // Some validation, not enough though\r
+ ClusteringSupport cs = session.getService(ClusteringSupport.class);\r
+ long cluster = cs.getCluster(r);\r
+ if(cluster > 0) {\r
+ changeLocation(r);\r
+ }\r
+ } catch (NumberFormatException e1) {\r
+ // Ignore, may happen for crap input\r
+ setStatus(DONT_TOUCH, "Invalid input, expected transient resource ID");\r
+ } catch (Exception e1) {\r
+ ErrorLogger.defaultLogError(e1);\r
+ setStatus(DONT_TOUCH, "Transient resource ID lookup failed. See Error Log.");\r
+ }\r
+ } else if (parts.length == 2) {\r
+ try {\r
+ int resourceIndex = Integer.parseInt(parts[1]);\r
+ long clusterId = Long.parseLong(parts[0]);\r
+ ClusteringSupport cs = session.getService(ClusteringSupport.class);\r
+ Resource r = cs.getResourceByIndexAndCluster(resourceIndex, clusterId);\r
+ changeLocation(r);\r
+ } catch (NumberFormatException e1) {\r
+ // Ignore, may happen for crap input\r
+ setStatus(DONT_TOUCH, "Invalid input, expected index & cluster IDs");\r
+ } catch (Exception e1) {\r
+ ErrorLogger.defaultLogError(e1);\r
+ setStatus(DONT_TOUCH, "Index & cluster -based lookup failed. See Error Log.");\r
+ }\r
+ }\r
+\r
+ // Try to see if the input data is an URI reference\r
+ try {\r
+ // First check that the input really is a proper URI.\r
+ String uri = input;\r
+ if (!input.equals("http:/") && input.endsWith("/"))\r
+ uri = input.substring(0, input.length() - 1);\r
+ new URI(uri);\r
+ Resource r = session.syncRequest( Queries.resource( uri ) );\r
+ changeLocation(r);\r
+ return;\r
+ } catch (URISyntaxException e) {\r
+ // Ignore, this is not a proper URI at all.\r
+ } catch (ResourceNotFoundException e1) {\r
+ // Ok, this was an URI, but no resource was found.\r
+ setStatus(DONT_TOUCH, "Resource for URI '" + input + "' not found");\r
+ return;\r
+ } catch (DatabaseException e1) {\r
+ setStatus(DONT_TOUCH, "URI lookup failed. See Error Log.");\r
+ ErrorLogger.defaultLogError(e1);\r
+ }\r
+\r
+ setStatus(DONT_TOUCH, "Invalid input, resource ID or URI expected");\r
+ }\r
+\r
+ public Label createDropLabel(Composite parent) {\r
+ final Label label = new Label(parent, SWT.BORDER);\r
+ label.setAlignment(SWT.CENTER);\r
+ label.setText("Drag a resource here to examine it in this debugger!");\r
+ label.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));\r
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).hint(SWT.DEFAULT, 20).span(2, 1).grab(true, false).applyTo(label);\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
+ ResourceArray[] data = parseEventData(event);\r
+ if (data == null || data.length != 1) {\r
+ event.detail = DND.DROP_NONE;\r
+ return;\r
+ }\r
+ final ResourceArray array = data[0];\r
+ final Resource r = array.resources[array.resources.length - 1];\r
+\r
+ changeLocation(r);\r
+ }\r
+\r
+ private ResourceArray[] parseEventData(DropTargetEvent event) {\r
+ //System.out.println("DATA: " + event.data);\r
+ if (event.data instanceof String) {\r
+ try {\r
+ SerialisationSupport support = session.getService(SerialisationSupport.class);\r
+ return ResourceTransferUtils.readStringTransferable(support, (String) event.data).toResourceArrayArray();\r
+ } catch (IllegalArgumentException e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ } catch (DatabaseException e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ }\r
+ }\r
+ ResourceArray[] ret = ResourceAdaptionUtils.toResourceArrays(event.data);\r
+ if (ret.length > 0)\r
+ return ret;\r
+ return null;\r
+ }\r
+ });\r
+\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
+ GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(browser);\r
+\r
+ // Left/right arrows for back/forward\r
+ browser.addKeyListener(new KeyAdapter() {\r
+// \r
+// @Override\r
+// public void keyPressed(KeyEvent e) {\r
+// if (e.keyCode == SWT.F5) {\r
+// refreshBrowser();\r
+// }\r
+// }\r
+// }\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
+ \r
+ if (e.keyCode == SWT.F5) {\r
+ refreshBrowser();\r
+ }\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
+ Resource element = links.getRight(target);\r
+ if (element == currentElement) {\r
+ event.doit = false;\r
+ return;\r
+ }\r
+ changeLocation(element);\r
+ } else if (location.startsWith("about:-remove")) {\r
+ String target = location.replace("about:-remove", "");\r
+ String n[] = target.split(STATEMENT_PART_SEPARATOR);\r
+ if (n.length != 3)\r
+ return;\r
+\r
+ final Resource s = links.getRight(n[0]);\r
+ final Resource p = links.getRight(n[1]);\r
+ final Resource o = links.getRight(n[2]);\r
+\r
+ // Make sure this is what the use wants.\r
+ MessageDialog md = new MessageDialog(\r
+ getShell(),\r
+ "Confirm action...",\r
+ null,\r
+ "This action will remove the selected statement.\nAre you sure you want to proceed with this action?",\r
+ MessageDialog.QUESTION, new String[] { "Cancel", "Continue" }, 0);\r
+ if (md.open() != 1) {\r
+ return;\r
+ }\r
+\r
+ session.asyncRequest(new WriteRequest() {\r
+\r
+ @Override\r
+ public void perform(WriteGraph g) throws DatabaseException {\r
+ try {\r
+ List<Resource> ls = OrderedSetUtils.toList(g, s);\r
+ if(ls.contains(o))\r
+ OrderedSetUtils.remove(g, s, o);\r
+ } catch (DatabaseException e) {\r
+ \r
+ }\r
+ try {\r
+ List<Resource> ls = ListUtils.toList(g, s);\r
+ if(ls.contains(o))\r
+ ListUtils.removeElement(g, s, o);\r
+ } catch (DatabaseException e) {\r
+ \r
+ }\r
+ g.denyStatement(s, p, o);\r
+ }\r
+\r
+ }, new Callback<DatabaseException>() {\r
+\r
+ @Override\r
+ public void run(DatabaseException parameter) {\r
+ refreshBrowser();\r
+ }\r
+\r
+ });\r
+ } else if (location.startsWith("about:-edit-value")) {\r
+ String target = location.replace("about:-edit-value", "");\r
+ final Resource o = links.getRight(target);\r
+\r
+ session.asyncRequest(new ReadRequest() {\r
+\r
+ String previousValue;\r
+\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+\r
+ previousValue = getResourceName(graph, o);\r
+ final StringModifier modifier = graph.adapt(o, StringModifier.class);\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ InputDialog dialog = new InputDialog(\r
+ getShell(),\r
+ "Edit Value",\r
+ null,\r
+ previousValue,\r
+ new IInputValidator() {\r
+ @Override\r
+ public String isValid(String newText) {\r
+ return modifier.isValid(newText);\r
+ }\r
+ }) {\r
+ private static final String DIALOG = "DebuggerEditValueDialog"; //$NON-NLS-1$\r
+ private IDialogSettings dialogBoundsSettings;\r
+ @Override\r
+ protected IDialogSettings getDialogBoundsSettings() {\r
+ if (dialogBoundsSettings == null) {\r
+ IDialogSettings settings = Activator.getDefault().getDialogSettings();\r
+ dialogBoundsSettings = settings.getSection(DIALOG);\r
+ if (dialogBoundsSettings == null)\r
+ dialogBoundsSettings = settings.addNewSection(DIALOG);\r
+ }\r
+ return dialogBoundsSettings;\r
+ }\r
+ @Override\r
+ protected Point getInitialSize() {\r
+ Point defaultSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);\r
+ Point result = super.getInitialSize();\r
+ if (defaultSize.equals(result))\r
+ return new Point(600, 400);\r
+ return result;\r
+ }\r
+ protected int getShellStyle() {\r
+ return super.getShellStyle() | SWT.RESIZE;\r
+ }\r
+ protected org.eclipse.swt.widgets.Control createDialogArea(Composite parent) {\r
+ Composite composite = (Composite) super.createDialogArea(parent);\r
+ getText().setLayoutData(\r
+ new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL\r
+ | GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));\r
+ {\r
+ Label label = new Label(composite, SWT.NONE);\r
+ label.moveAbove(getText());\r
+ label.setText("Input new property value. For numeric vector values, separate numbers with comma (',').");\r
+ GridData data = new GridData(GridData.GRAB_HORIZONTAL\r
+ | GridData.HORIZONTAL_ALIGN_FILL\r
+ | GridData.VERTICAL_ALIGN_CENTER);\r
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);\r
+ label.setLayoutData(data);\r
+ label.setFont(parent.getFont());\r
+ }\r
+ return composite;\r
+ }\r
+ protected int getInputTextStyle() {\r
+ return SWT.MULTI | SWT.BORDER;\r
+ }\r
+ };\r
+ int ok = dialog.open();\r
+ if (ok != Dialog.OK)\r
+ return;\r
+\r
+ final String value = dialog.getValue();\r
+ session.asyncRequest(new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph g) throws DatabaseException {\r
+ //modifier.modify( g, htmlEscape( value ) );\r
+ modifier.modify( g, value );\r
+ }\r
+ }, new Callback<DatabaseException>() {\r
+ @Override\r
+ public void run(DatabaseException parameter) {\r
+ if (parameter != null)\r
+ ErrorLogger.defaultLogError(parameter);\r
+ refreshBrowser();\r
+ }\r
+ });\r
+ return;\r
+ }\r
+ });\r
+ }\r
+\r
+ }, new ProcedureAdapter<Object>() {\r
+ @Override\r
+ public void exception(Throwable t) {\r
+ ErrorLogger.defaultLogError(t);\r
+ }\r
+ });\r
+\r
+ }\r
+ }\r
+ });\r
+\r
+ // Schedule a request that updates the browser content.\r
+ refreshBrowser();\r
+ GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);\r
+ support.removeListener(changeListener);\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
+ updater.asyncRequest(new ReadRequest() {\r
+\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ updateContent(graph, currentElement);\r
+ }\r
+\r
+ });\r
+\r
+ }\r
+\r
+ public Resource getDebuggerLocation() {\r
+ return currentElement;\r
+ }\r
+\r
+ public void changeLocation(Resource element) {\r
+ if (currentElement != null) {\r
+ backHistory.addLast(currentElement);\r
+ }\r
+ currentElement = element;\r
+ forwardHistory.clear();\r
+\r
+ refreshBrowser();\r
+ setStatus(DONT_TOUCH, null);\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
+ public static String htmlEscape(String s)\r
+ {\r
+ return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "<br/>");\r
+ }\r
+\r
+ /**\r
+ * Get resource name(?) \r
+ * \r
+ * @param graph\r
+ * @param r\r
+ * @return\r
+ */\r
+ protected String getResourceName(ReadGraph graph, Resource r) {\r
+ try {\r
+\r
+ String name = null;\r
+ //System.out.println("hasValue(" + NameUtils.getSafeName(graph, r, true));\r
+ if (graph.hasValue(r)) {\r
+ // too large array may cause application to run out of memory.\r
+ //System.out.println("getValue(" + NameUtils.getSafeName(graph, r, true));\r
+ Datatype type = graph.getPossibleRelatedValue(r, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
+ if (type!=null) {\r
+ Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class );\r
+ if (type.equals( rviBinding.type() )) {\r
+ RVI rvi = graph.getValue(r, rviBinding);\r
+ \r
+ try {\r
+ Variable v = Variables.getConfigurationContext( graph, currentElement );\r
+ name = rvi.asString(graph, v);\r
+// name = rvi.resolve(graph, v).getURI(graph);\r
+ } catch (DatabaseException dbe ) {\r
+ name = rvi.toString( graph );\r
+ }\r
+ } else {\r
+ long valueSize = NameUtils.getPossibleValueSize(graph, r);\r
+ if (valueSize > RESOURCE_NAME_MAX_LENGTH) {\r
+// Binding b = Bindings.getBinding(type);\r
+// Object v = graph.getValue(r, b);\r
+// Serializer s = Bindings.getSerializerUnchecked(b);\r
+// int size = s.getSize(v);\r
+ name = "Approx. " + valueSize + " byte literal of type " + type.toSingleLineString();\r
+ } else { \r
+ Binding b = Bindings.getBinding(type);\r
+ Object v = graph.getValue(r, b);\r
+ if (b.type() instanceof StringType) {\r
+ name = (String) graph.getValue(r, b);\r
+ } else {\r
+ name = b.toString(v, false);\r
+ }\r
+ if (type instanceof ArrayType){\r
+ name = name.substring(1, name.length()-1);\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ Object o = graph.getValue(r);\r
+ name = toName(o);\r
+ }\r
+ \r
+ if(name.isEmpty()) {\r
+ name = "<empty value>";\r
+ }\r
+ \r
+ }\r
+ // Does resource have a file ??\r
+ if (name == null) {\r
+// try {\r
+// Accessor accessor = graph.getAccessor(r);\r
+// name = "File of type " + accessor.type().toSingleLineString();\r
+// } catch (DatabaseException e) {\r
+// // No file, try next alternative.\r
+// }\r
+ }\r
+ if (name == null) {\r
+ //name = graph.adapt(r, String.class);\r
+ //if(name.isEmpty())\r
+ name = DebugUtils.getSafeLabel(graph, r);\r
+ if (name.isEmpty())\r
+ name = "<empty name>";\r
+ }\r
+// ClusteringSupport support = graph.getSession().getService(ClusteringSupport.class);\r
+// if(name == null)\r
+// return "[" + r.getResourceId() + " - " + support.getCluster(r) + "]";\r
+ if(displayClusters) {\r
+// SessionDebug debug = graph.getSession().getDebug();\r
+// name += " (" + debug.getCluster(r) + ")";\r
+ }\r
+ return name;\r
+ } catch (AdaptionException e) {\r
+// e.printStackTrace();\r
+ String name = safeReadableString(graph, r);\r
+// try {\r
+// MessageService.defaultLog(new DetailStatus(IDetailStatus.DEBUG, Activator.PLUGIN_ID, 0, NLS.bind(Messages.Name_adaption_problem, MessageUtil.resource(session, r, "this resource")), e));\r
+// } catch (ReferenceSerializationException e1) {\r
+// e1.printStackTrace();\r
+// ErrorLogger.defaultLogWarning(e1);\r
+// }\r
+ return name;\r
+ } catch (Exception e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ String name = safeReadableString(graph, r);\r
+// try {\r
+// MessageService.defaultLog(new DetailStatus(IDetailStatus.DEBUG, Activator.PLUGIN_ID, 0, NLS.bind(Messages.Name_formulation_problem, MessageUtil.resource(session, r, "this resource")), e));\r
+// } catch (ReferenceSerializationException e1) {\r
+// e1.printStackTrace();\r
+// ErrorLogger.defaultLogWarning(e1);\r
+// }\r
+ return name;\r
+ }\r
+ }\r
+\r
+ private String getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {\r
+ String name;\r
+ try {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ if (graph.isInstanceOf(r, L0.Assertion)) {\r
+ Resource pred = graph.getSingleObject(r, L0.HasPredicate);\r
+ Resource obj = graph.getSingleObject(r, L0.HasObject);\r
+ String tmp = htmlEscape( getResourceName(graph, pred) + " -> " + getResourceName(graph, obj) + " (Assertion)" );\r
+ name = tmp.substring(0, Math.min(80, tmp.length()));\r
+ } else {\r
+ String resourceName = getResourceName(graph, r);\r
+ if(resourceName.equals("Inverse")) {\r
+ Resource inverse = graph.getPossibleInverse(r);\r
+ if(inverse != null && graph.hasStatement(inverse, L0.ConsistsOf, r))\r
+ resourceName = getResourceName(graph, inverse) + "/Inverse";\r
+ }\r
+ String tmp = htmlEscape( resourceName );\r
+ name = tmp.substring(0, Math.min(80, tmp.length()));\r
+ }\r
+ \r
+ } catch (OutOfMemoryError e) {\r
+ name = "OutOfMemoryError";\r
+ }\r
+ String ret = "<a href=\"simantics:browser-link" + getLinkString(r) + "\">"\r
+ + name\r
+ + "</a>";\r
+ if (graph.isInstanceOf(r, L0.Literal)) {\r
+ ret += " <a class=\"edit-link\" href=\"simantics:browser-edit-value" + getLinkString(r) + "\">"\r
+ + "(edit)"\r
+ + "</a>";\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ private String getStatementRemoveRef(Resource s, Resource p, Resource o) {\r
+ return "<a href=\"simantics:browser-remove" + getStatementString(s, p, o)\r
+ + "\" title=\"Remove this statement\">X</a>";\r
+ }\r
+\r
+ private void updatePred(StringBuffer content, ReadGraph graph, Resource subj, Resource pred, List<Resource[]> stats) throws DatabaseException {\r
+ // Generate output content from statements\r
+ String[][] objects = new String[stats.size()][];\r
+ for (int i = 0; i < stats.size(); ++i) {\r
+ Resource stmSubject = stats.get(i)[0];\r
+ Resource object = stats.get(i)[1];\r
+\r
+ objects[i] = new String[4];\r
+ objects[i][0] = getLinkString(object);\r
+ objects[i][1] = htmlEscape( getResourceName(graph, object) );\r
+ objects[i][2] = getResourceRef(graph, object);\r
+\r
+ // Make a note if the statement was acquired.\r
+ if(!stmSubject.equals(subj)) {\r
+ objects[i][3] = " (in " + getResourceRef(graph, stmSubject) + ")";\r
+ }\r
+ }\r
+\r
+ // Sort statements by object name\r
+ Arrays.sort(objects, new Comparator<String[]>() {\r
+ @Override\r
+ public int compare(String[] o1, String[] o2) {\r
+ return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1[1], o2[1]);\r
+ }\r
+ });\r
+\r
+ // Output table rows\r
+ for (int i = 0; i < objects.length; ++i) {\r
+ content.append("<tr>");\r
+ // Predicate column\r
+ if (i == 0)\r
+ content.append("<td rowspan=\"").append(objects.length).append("\" valign=\"top\">").append(getResourceRef(graph, pred)).append("</td>");\r
+\r
+ // Object column\r
+ if (objects[i][3] == null) content.append("<td>");\r
+ else content.append("<td class=\"acquired\">");\r
+\r
+ content.append(objects[i][2]);\r
+ if (objects[i][3] != null)\r
+ content.append(objects[i][3]);\r
+\r
+ content.append("</td>");\r
+ \r
+ VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);\r
+ VirtualGraph vg = vgs.getGraph(graph, subj, pred, links.getRight(objects[i][0]));\r
+ \r
+ if(vg != null) {\r
+ content.append("<td>").append(vg.toString()).append("</td>");\r
+ } else {\r
+ content.append("<td>DB</td>");\r
+ }\r
+ \r
+\r
+ // Statement remove -link column\r
+ // Only allowed for non-acquired statements.\r
+ if (objects[i][3] == null) {\r
+ content.append("<td class=\"remove\">");\r
+ content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));\r
+ content.append("</td>");\r
+ }\r
+ content.append("</tr>");\r
+ }\r
+ }\r
+\r
+ private void updateTag(StringBuffer content, ReadGraph graph, Resource subj, Resource tag) throws DatabaseException {\r
+\r
+ // Generate output content from statements\r
+ String ref = getResourceRef(graph, tag);\r
+\r
+ content.append("<tr>");\r
+ content.append("<td rowspan=\"1\" colspan=\"3\" valign=\"top\">").append(ref).append("</td>");\r
+ //content.append("<td>" + name + "</td>");\r
+ content.append("<td class=\"remove\">");\r
+ content.append(getStatementRemoveRef(subj, tag, subj));\r
+ content.append("</td>");\r
+ content.append("</tr>");\r
+\r
+ }\r
+\r
+ private void updateOrderedSet(StringBuffer content, ReadGraph graph, Resource subj) throws DatabaseException {\r
+ //List<Resource> list = OrderedSetUtils.toList(graph, subj);\r
+ /*\r
+ // Generate output content from statements\r
+ String[][] objects = new String[stats.size()][];\r
+ for (int i = 0; i < stats.size(); ++i) {\r
+ Resource stmSubject = stats.get(i)[0];\r
+ Resource object = stats.get(i)[1];\r
+\r
+ objects[i] = new String[4];\r
+ objects[i][0] = getLinkString(object);\r
+ objects[i][1] = getResourceName(graph, object);\r
+ objects[i][2] = getResourceRef(graph, object);\r
+\r
+ // Make a note if the statement was acquired.\r
+ if(!stmSubject.equals(subj)) {\r
+ objects[i][3] = " (acquired from " + getResourceRef(graph, stmSubject) + ")";\r
+ }\r
+ }\r
+\r
+ // Sort statements by object name\r
+ Arrays.sort(objects, new Comparator<String[]>() {\r
+ @Override\r
+ public int compare(String[] o1, String[] o2) {\r
+ return o1[1].compareTo(o2[1]);\r
+ }\r
+ });*/\r
+\r
+ List<String> list = new ArrayList<String>();\r
+ Resource cur = subj;\r
+ while(true) {\r
+ try {\r
+ cur = OrderedSetUtils.next(graph, subj, cur);\r
+ } catch(DatabaseException e) {\r
+ list.add("<span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">" + e.getMessage() + "</span>");\r
+ Resource inv = graph.getPossibleInverse(subj);\r
+ for(Statement stat : graph.getStatements(cur, L0.IsRelatedTo)) {\r
+ if(stat.getSubject().equals(cur)) {\r
+ if(stat.getPredicate().equals(subj)) {\r
+ list.add("next " + getResourceRef(graph, stat.getObject()));\r
+ }\r
+ else if(stat.getPredicate().equals(inv)) {\r
+ list.add("prev " + getResourceRef(graph, stat.getObject()));\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ if(cur.equals(subj))\r
+ break;\r
+ list.add(getResourceRef(graph, cur));\r
+ }\r
+\r
+ // Output table rows\r
+ for (int i = 0; i < list.size() ; ++i) {\r
+ content.append("<tr>");\r
+ // Predicate column\r
+ if (i == 0)\r
+ content.append("<td rowspan=\"").append(list.size()).append("\" valign=\"top\">Ordered Set Elements</td>");\r
+\r
+ // Object column\r
+ content.append("<td>");\r
+ content.append(list.get(i));\r
+ content.append("</td>");\r
+\r
+ // Statement remove -link column\r
+ // Only allowed for non-acquired statements.\r
+ /*if (objects[i][3] == null) {\r
+ content.append("<td class=\"remove\">");\r
+ content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));\r
+ content.append("</td>");\r
+ }*/\r
+ content.append("</tr>");\r
+ }\r
+ }\r
+\r
+ private void updateLinkedList(StringBuffer content, ReadGraph graph, Resource subj) throws DatabaseException {\r
+\r
+ List<String> list = new ArrayList<String>();\r
+ \r
+ try {\r
+ List<Resource> resources = ListUtils.toList(graph, subj);\r
+ for(Resource element : resources) {\r
+ list.add(getResourceRef(graph, element));\r
+ }\r
+ } catch (DatabaseException e) {\r
+ throw new ValidationException(e);\r
+ }\r
+\r
+ // Output table rows\r
+ for (int i = 0; i < list.size() ; ++i) {\r
+ content.append("<tr>");\r
+ // Predicate column\r
+ if (i == 0)\r
+ content.append("<td rowspan=\"").append(list.size()).append("\" valign=\"top\">Linked List Elements</td>");\r
+\r
+ // Object column\r
+ content.append("<td>");\r
+ content.append(list.get(i));\r
+ content.append("</td><td>DB</td>");\r
+\r
+ // Statement remove -link column\r
+ // Only allowed for non-acquired statements.\r
+ /*if (objects[i][3] == null) {\r
+ content.append("<td class=\"remove\">");\r
+ content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));\r
+ content.append("</td>");\r
+ }*/\r
+ content.append("</tr>");\r
+ }\r
+ }\r
+\r
+ protected synchronized void updateContent(final ReadGraph graph, Resource... resources) throws DatabaseException {\r
+ L0 = Layer0.getInstance(graph);\r
+\r
+ links.clear();\r
+ StringBuffer content = new StringBuffer();\r
+\r
+ // Generate HTML -page\r
+ content.append("<html>\n<head>\n")\r
+ .append(getHead())\r
+ .append("\n</head>\n")\r
+ .append("<body>\n")\r
+ .append("<div id=\"mainContent\">\n\n");\r
+\r
+ for (Resource r : resources) {\r
+ if (r == null)\r
+ continue;\r
+\r
+ String uri = null;\r
+ try {\r
+ uri = graph.syncRequest(new ResourceToPossibleURI(r));\r
+ } catch (Exception e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ uri = "Cannot get URI: " + e.getMessage();\r
+ }\r
+\r
+ // Top DIV\r
+ content.append("<div id=\"top\">\n");\r
+ content.append("<table class=\"top\">\n");\r
+ if (uri != null) {\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
+ }\r
+\r
+ XSupport xs = graph.getService(XSupport.class);\r
+ boolean immutable = xs.getImmutable(r);\r
+\r
+ Collection<Statement> statements = graph.getStatements(r, L0.IsWeaklyRelatedTo);\r
+ HashMultiMap<Resource, Resource[]> map = new HashMultiMap<Resource, Resource[]>();\r
+ for(org.simantics.db.Statement statement : statements) {\r
+ Resource predicate = null;\r
+ Resource subject = null;\r
+ Resource obj = null;\r
+ try {\r
+ predicate = statement.getPredicate();\r
+ subject = statement.getSubject();\r
+ obj = statement.getObject();\r
+ map.add(predicate, new Resource[] {subject, obj});\r
+ } catch (Throwable e) {\r
+ ErrorLogger.defaultLogError("Cannot find statement " + subject + " " + predicate + " " + obj, e);\r
+ }\r
+ }\r
+ SerialisationSupport ss = graph.getSession().getService(SerialisationSupport.class);\r
+ ClusteringSupport support = graph.getSession().getService(ClusteringSupport.class);\r
+ content.append("<tr><td class=\"top_key\">Identifiers</td><td class=\"top_value\">");\r
+ content.append("<span id=\"resource_id\">")\r
+ .append(" RID = $").append(r.getResourceId())\r
+ .append(" Resource Key = ").append(ss.getTransientId(r))\r
+ .append(" CID = ").append(support.getCluster(r));\r
+ content.append("</span></td>");\r
+ if (immutable)\r
+ content.append("<td class=\"remove\">[IMMUTABLE]</td>");\r
+ content.append("</tr>\n");\r
+ \r
+ boolean isClusterSet = support.isClusterSet(r);\r
+ Resource parentSet = support.getClusterSetOfCluster(r);\r
+ String parentSetURI = parentSet != null ? graph.getPossibleURI(parentSet) : null;\r
+ \r
+ content.append("<tr><td class=\"top_key\">Clustering</td><td class=\"top_value\">");\r
+ content.append("<span id=\"resource_id\">");\r
+ \r
+ if(parentSetURI != null)\r
+ content.append(" Containing cluster set = ").append(parentSetURI);\r
+ else if (parentSet != null)\r
+ content.append(" Containing cluster set = ").append(parentSet.toString());\r
+ else \r
+ content.append(" Not in any cluster set ");\r
+ \r
+ content.append("</span></td>");\r
+ if (isClusterSet)\r
+ content.append("<td class=\"remove\">[CLUSTER SET]</td>");\r
+ content.append("</tr>\n");\r
+ \r
+ // If the resource has a value, show it.\r
+ String resourceValue = getResourceValue(graph, r);\r
+ if (resourceValue != null) {\r
+ content\r
+ .append("<tr><td class=\"top_key\">Attached value</td><td class=\"top_value\">")\r
+ .append(htmlEscape(resourceValue))\r
+ .append("</td></tr>\n");\r
+ }\r
+\r
+ // Close #top\r
+ content.append("</table>\n");\r
+ content.append("</div>\n");\r
+\r
+ content.append("\n<div id=\"data\">\n");\r
+ content.append("<table>\n")\r
+ .append("<tr><th>Predicate</th><th>Object</th><th>Graph</th></tr>")\r
+ .append("<tr><td class=\"subtitle\" colspan=\"3\">Basic information</td></tr>");\r
+\r
+ boolean isOrderedSet = graph.isInstanceOf(r, L0.OrderedSet);\r
+ boolean isLinkedList = graph.isInstanceOf(r, L0.List);\r
+// map.remove(r);\r
+\r
+ // BASIC INFORMATION:\r
+ for (Resource pred :\r
+ new Resource[] {L0.HasName, L0.InstanceOf,\r
+ L0.Inherits, L0.SubrelationOf,\r
+ L0.PartOf, L0.ConsistsOf})\r
+ if (map.containsKey(pred))\r
+ updatePred(content, graph, r, pred, map.remove(pred));\r
+\r
+ // TAGS\r
+ content.append("<tr><td class=\"subtitle\" colspan=\"3\">Tags</td></tr>");\r
+ for(Statement stm : statements) {\r
+ if(stm.getSubject().equals(stm.getObject())) {\r
+ updateTag(content, graph, r, stm.getPredicate());\r
+ map.remove(stm.getPredicate());\r
+ }\r
+ }\r
+\r
+ // ORDERED SETS\r
+ content.append("<tr><td class=\"subtitle\" colspan=\"3\">Ordered Sets</td></tr>");\r
+ for(Statement stm : statements) {\r
+ Resource predicate = stm.getPredicate();\r
+ if(graph.isInstanceOf(stm.getPredicate(), L0.OrderedSet)) {\r
+ updateTag(content, graph, r, stm.getPredicate());\r
+ if(map.get(stm.getPredicate()) != null && map.get(stm.getPredicate()).size() == 1)\r
+ map.remove(stm.getPredicate());\r
+ }\r
+ Resource inverse = graph.getPossibleInverse(predicate);\r
+ if (inverse != null) {\r
+ if(graph.isInstanceOf(inverse, L0.OrderedSet)) {\r
+ if(map.get(stm.getPredicate()) != null && map.get(stm.getPredicate()).size() == 1)\r
+ map.remove(stm.getPredicate());\r
+ }\r
+ } else {\r
+ // FIXME : should we infor missing inverse\r
+ }\r
+ }\r
+\r
+ // IS RELATED TO\r
+ content.append("<tr><td class=\"subtitle\" colspan=\"3\">Is Related To</td></tr>");\r
+\r
+ // ELEMENTS OF ORDERED SET\r
+ if(isOrderedSet) {\r
+ //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");\r
+ try {\r
+ updateOrderedSet(content, graph, r);\r
+ } catch (ValidationException e) {\r
+ content.append("<td colspan=\"3\"><span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">").append(e.getMessage()).append("</span></td>");\r
+ }\r
+ }\r
+\r
+ // ELEMENTS OF LINKED LIST\r
+ if(isLinkedList) {\r
+ //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");\r
+ try {\r
+ updateLinkedList(content, graph, r);\r
+ } catch (ValidationException e) {\r
+ content.append("<td colspan=\"3\"><span style=\"color:red;font-weight:bold\">BROKEN LINKED LIST:<br/></span><span style=\"color:red\">").append(e.getMessage()).append("</span></td>");\r
+ }\r
+ }\r
+\r
+ // IS RELATED TO (other)\r
+ Resource[] preds = map.keySet().toArray(new Resource[0]);\r
+ final Map<Resource, String> strmap = new HashMap<Resource, String>(preds.length);\r
+ for(Resource pred : preds) {\r
+ String str = htmlEscape( getResourceName(graph, pred) );\r
+ if(str == null)\r
+ str = "<null>";\r
+ strmap.put(pred, str);\r
+ }\r
+ Arrays.sort(preds, new Comparator<Resource>() {\r
+ @Override\r
+ public int compare(Resource o1, Resource o2) {\r
+ return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(strmap.get(o1), strmap.get(o2));\r
+ }\r
+ });\r
+ for(Resource pred : preds)\r
+ if(graph.isSubrelationOf(pred, L0.IsRelatedTo))\r
+ updatePred(content, graph, r, pred, map.get(pred));\r
+\r
+ // OTHER STATEMENTS\r
+ content.append("<tr><td class=\"subtitle\" colspan=\"3\">Other statements</td></tr>");\r
+ for(Resource pred : preds)\r
+ if(!graph.isSubrelationOf(pred, L0.IsRelatedTo))\r
+ updatePred(content, graph, r, pred, map.get(pred));\r
+ content.append("</table>\n");\r
+ }\r
+ // Close #data\r
+ content.append("</div>\n\n");\r
+ // Close #mainContent\r
+ content.append("</div>\n");\r
+ content.append("</body>\n</html>\n");\r
+\r
+ // Update content\r
+ final String finalContent = content.toString();\r
+ if (!isDisposed()) {\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (!browser.isDisposed())\r
+ browser.setText(finalContent);\r
+ }\r
+ });\r
+ }\r
+ }\r
+\r
+ private String getResourceValue(ReadGraph graph, Resource r) {\r
+ try {\r
+ if (graph.hasValue(r)) {\r
+ // too large array may cause application to run out of memory.\r
+ //System.out.println("getValue(" + NameUtils.getSafeName(graph, r, true));\r
+ Datatype type = graph.getPossibleRelatedValue(r, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
+ if (type != null) {\r
+ Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class );\r
+ if (type.equals( rviBinding.type() )) {\r
+ RVI rvi = graph.getValue(r, rviBinding);\r
+ try {\r
+ Variable v = Variables.getConfigurationContext( graph, r );\r
+ return rvi.asString(graph, v);\r
+ } catch (DatabaseException dbe ) {\r
+ return rvi.toString( graph );\r
+ }\r
+ } else {\r
+ Binding b = Bindings.getBinding(type);\r
+ Object v = graph.getValue(r, b);\r
+ Serializer s = Bindings.getSerializerUnchecked(b);\r
+ int size = s.getSize(v);\r
+ if (size > RESOURCE_VALUE_MAX_SIZE) {\r
+ return "Approx. " + size + " byte literal of type " + type.toSingleLineString();\r
+ } else {\r
+ return b.toString(v, false);\r
+ }\r
+ }\r
+ } else {\r
+ Object o = graph.getValue(r);\r
+ return toName(o);\r
+ }\r
+ }\r
+ return null;\r
+ } catch (DatabaseException e) {\r
+ return e.getMessage();\r
+ } catch (IOException e) {\r
+ return e.getMessage();\r
+ } catch (BindingException e) {\r
+ return e.getMessage();\r
+ }\r
+ }\r
+\r
+ private static String safeReadableString(ReadGraph g, Resource r) {\r
+ try {\r
+ return NameUtils.getSafeName(g, r);\r
+ } catch(Throwable throwable) {\r
+ ErrorLogger.defaultLogError(throwable);\r
+ return "<font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font>";\r
+ }\r
+ }\r
+\r
+// private static String safeReadableString(IEntity t) {\r
+// try {\r
+// return ResourceDebugUtils.getReadableNameForEntity(t);\r
+// } catch(Throwable throwable) {\r
+// throwable.printStackTrace();\r
+// return "<font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font>";\r
+// }\r
+// }\r
+\r
+ private String getLinkString(Container<Resource> t) {\r
+ String link = links.getLeft(t.get());\r
+ if(link == null) {\r
+ link = UUID.randomUUID().toString();\r
+ links.map(link, t.get());\r
+ }\r
+ return link;\r
+ }\r
+\r
+// private String getPropertyEditString(Container<Resource> t) {\r
+// String link = links.getLeft(t.get());\r
+// if(link == null) {\r
+// link = UUID.randomUUID().toString();\r
+// links.map(link, t.get());\r
+// }\r
+// return link;\r
+// }\r
+\r
+ private String getStatementString(Resource _s, Resource _p, Resource _o) {\r
+ String s = getLinkString(_s);\r
+ String p = getLinkString(_p);\r
+ String o = getLinkString(_o);\r
+ return s + STATEMENT_PART_SEPARATOR + p + STATEMENT_PART_SEPARATOR + o;\r
+ }\r
+\r
+// private String getStatementString(Statement stm) {\r
+// String s = getLinkString(stm.getSubject());\r
+// String p = getLinkString(stm.getPredicate());\r
+// String o = getLinkString(stm.getObject());\r
+// return s + STATEMENT_PART_SEPARATOR + p + STATEMENT_PART_SEPARATOR + o;\r
+// }\r
+\r
+// private String toOutgoingTableRow(IEntity subject, Statement stm) {\r
+// boolean isAcquired = !subject.equals(stm.getSubject());\r
+//\r
+// String plainCell = "%s";\r
+// String hrefCell = "<a href=\"%s\">%s</a>";\r
+// String formatTemplate = isAcquired\r
+// ? "<tr>\n<td class=\"acquired\">{0}</td>\n<td class=\"acquired\">{1}</td>\n<td class=\"acquired\">{1}</td>\n<td class=\"acquired\">{1}</td>\n</tr>"\r
+// : "<tr>\n<td>{1}</td>\n<td>{1}</td>\n<td>{1}</td>\n<td>{1}</td>\n</tr>";\r
+// String format = NLS.bind(formatTemplate, plainCell, hrefCell);\r
+//// System.out.println("format: " + format);\r
+//\r
+// IEntity s = stm.getSubject();\r
+// IEntity p = stm.getPredicate();\r
+// IEntity o = stm.getObject();\r
+//\r
+//// String timePart = "[" + timeToString(t.getBegin()) + ", " + timeToString(t.getEnd()) + "]";\r
+//\r
+// try {\r
+// return !isAcquired\r
+// ? String.format(format,\r
+// "about:blank-remove" + getStatementString(stm), "Remove",\r
+// "about:blank-link" + getLinkString(s), safeReadableString(s),\r
+// "about:blank-link" + getLinkString(p), safeReadableString(p),\r
+// "about:blank-link" + getLinkString(o), safeReadableString(o))\r
+// : String.format(format,\r
+// "Acquired",\r
+// "about:blank-link" + getLinkString(s), safeReadableString(s),\r
+// "about:blank-link" + getLinkString(p), safeReadableString(p),\r
+// "about:blank-link" + getLinkString(o), safeReadableString(o)\r
+// );\r
+// } catch (Throwable throwable) {\r
+// return "<tr><td colspan=\"4\"><font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font></td></tr>";\r
+// }\r
+// }\r
+\r
+// private String intervalToString(Interval time) {\r
+// return timeToString(time.getBegin()) + ", " + timeToString(time.getEnd());\r
+// }\r
+//\r
+// DateFormat dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);\r
+//\r
+// private String timeToString(long time) {\r
+// if (time == Long.MIN_VALUE)\r
+// return "-∞";\r
+// if (time == Long.MAX_VALUE)\r
+// return "+∞";\r
+// //return String.valueOf(time);\r
+// Date d = new Date(time);\r
+// return dateTimeFormat.format(d);\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