1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.debug.ui;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.lang.reflect.Array;
19 import java.net.URISyntaxException;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.LinkedList;
27 import java.util.List;
29 import java.util.UUID;
30 import java.util.concurrent.CopyOnWriteArrayList;
32 import org.eclipse.core.runtime.Assert;
33 import org.eclipse.core.runtime.FileLocator;
34 import org.eclipse.core.runtime.IPath;
35 import org.eclipse.core.runtime.Path;
36 import org.eclipse.jface.action.IStatusLineManager;
37 import org.eclipse.jface.dialogs.Dialog;
38 import org.eclipse.jface.dialogs.IDialogConstants;
39 import org.eclipse.jface.dialogs.IDialogSettings;
40 import org.eclipse.jface.dialogs.IInputValidator;
41 import org.eclipse.jface.dialogs.InputDialog;
42 import org.eclipse.jface.dialogs.MessageDialog;
43 import org.eclipse.jface.layout.GridDataFactory;
44 import org.eclipse.jface.resource.ColorDescriptor;
45 import org.eclipse.jface.resource.JFaceResources;
46 import org.eclipse.jface.resource.LocalResourceManager;
47 import org.eclipse.osgi.util.NLS;
48 import org.eclipse.swt.SWT;
49 import org.eclipse.swt.SWTError;
50 import org.eclipse.swt.browser.Browser;
51 import org.eclipse.swt.browser.LocationAdapter;
52 import org.eclipse.swt.browser.LocationEvent;
53 import org.eclipse.swt.dnd.DND;
54 import org.eclipse.swt.dnd.DropTarget;
55 import org.eclipse.swt.dnd.DropTargetAdapter;
56 import org.eclipse.swt.dnd.DropTargetEvent;
57 import org.eclipse.swt.dnd.TextTransfer;
58 import org.eclipse.swt.dnd.Transfer;
59 import org.eclipse.swt.events.DisposeEvent;
60 import org.eclipse.swt.events.DisposeListener;
61 import org.eclipse.swt.events.FocusEvent;
62 import org.eclipse.swt.events.FocusListener;
63 import org.eclipse.swt.events.KeyAdapter;
64 import org.eclipse.swt.events.KeyEvent;
65 import org.eclipse.swt.events.ModifyEvent;
66 import org.eclipse.swt.events.ModifyListener;
67 import org.eclipse.swt.events.SelectionEvent;
68 import org.eclipse.swt.events.SelectionListener;
69 import org.eclipse.swt.graphics.Color;
70 import org.eclipse.swt.graphics.Point;
71 import org.eclipse.swt.graphics.RGB;
72 import org.eclipse.swt.layout.GridData;
73 import org.eclipse.swt.layout.GridLayout;
74 import org.eclipse.swt.widgets.Button;
75 import org.eclipse.swt.widgets.Composite;
76 import org.eclipse.swt.widgets.Label;
77 import org.eclipse.swt.widgets.Text;
78 import org.eclipse.ui.IWorkbenchSite;
79 import org.simantics.databoard.Bindings;
80 import org.simantics.databoard.Databoard;
81 import org.simantics.databoard.binding.Binding;
82 import org.simantics.databoard.binding.error.BindingException;
83 import org.simantics.databoard.serialization.Serializer;
84 import org.simantics.databoard.type.ArrayType;
85 import org.simantics.databoard.type.Datatype;
86 import org.simantics.databoard.type.StringType;
87 import org.simantics.databoard.util.ObjectUtils;
88 import org.simantics.db.AsyncRequestProcessor;
89 import org.simantics.db.ReadGraph;
90 import org.simantics.db.Resource;
91 import org.simantics.db.Session;
92 import org.simantics.db.Statement;
93 import org.simantics.db.VirtualGraph;
94 import org.simantics.db.WriteGraph;
95 import org.simantics.db.common.ResourceArray;
96 import org.simantics.db.common.procedure.adapter.ProcedureAdapter;
97 import org.simantics.db.common.request.Queries;
98 import org.simantics.db.common.request.ReadRequest;
99 import org.simantics.db.common.request.WriteRequest;
100 import org.simantics.db.common.uri.ResourceToPossibleURI;
101 import org.simantics.db.common.utils.ListUtils;
102 import org.simantics.db.common.utils.NameUtils;
103 import org.simantics.db.common.utils.OrderedSetUtils;
104 import org.simantics.db.event.ChangeEvent;
105 import org.simantics.db.event.ChangeListener;
106 import org.simantics.db.exception.AdaptionException;
107 import org.simantics.db.exception.DatabaseException;
108 import org.simantics.db.exception.ResourceNotFoundException;
109 import org.simantics.db.exception.ValidationException;
110 import org.simantics.db.layer0.adapter.StringModifier;
111 import org.simantics.db.layer0.variable.RVI;
112 import org.simantics.db.layer0.variable.Variable;
113 import org.simantics.db.layer0.variable.Variables;
114 import org.simantics.db.service.ClusteringSupport;
115 import org.simantics.db.service.GraphChangeListenerSupport;
116 import org.simantics.db.service.SerialisationSupport;
117 import org.simantics.db.service.VirtualGraphSupport;
118 import org.simantics.db.service.XSupport;
119 import org.simantics.debug.ui.internal.Activator;
120 import org.simantics.debug.ui.internal.DebugUtils;
121 import org.simantics.debug.ui.internal.HashMultiMap;
122 import org.simantics.layer0.Layer0;
123 import org.simantics.ui.dnd.LocalObjectTransfer;
124 import org.simantics.ui.dnd.ResourceReferenceTransfer;
125 import org.simantics.ui.dnd.ResourceTransferUtils;
126 import org.simantics.ui.utils.ResourceAdaptionUtils;
127 import org.simantics.utils.Container;
128 import org.simantics.utils.FileUtils;
129 import org.simantics.utils.datastructures.BijectionMap;
130 import org.simantics.utils.strings.AlphanumComparator;
131 import org.simantics.utils.ui.ErrorLogger;
132 import org.simantics.utils.ui.PathUtils;
133 import org.simantics.utils.ui.workbench.WorkbenchUtils;
136 public class GraphDebugger extends Composite {
138 public interface HistoryListener {
139 void historyChanged();
142 private static final String STATEMENT_PART_SEPARATOR = ","; //$NON-NLS-1$
143 private final static String DEFAULT_DEBUGGER_CSS_FILE = "debugger.css"; //$NON-NLS-1$
144 private final static String DEFAULT_DEBUGGER_CSS_PATH = "css/" + DEFAULT_DEBUGGER_CSS_FILE; //$NON-NLS-1$
146 private static int RESOURCE_NAME_MAX_LENGTH = 1000;
147 private static int RESOURCE_VALUE_MAX_SIZE = 16384;
149 private final LocalResourceManager resourceManager;
151 private String cssPath;
153 private Browser browser;
154 private final ColorDescriptor green = ColorDescriptor.createFrom(new RGB(0x57, 0xbc, 0x95));
156 private final boolean displayClusters = true;
158 private final BijectionMap<String, Resource> links = new BijectionMap<String, Resource>();
159 private final LinkedList<Resource> backHistory = new LinkedList<Resource>();
160 private final LinkedList<Resource> forwardHistory = new LinkedList<Resource>();
161 private Resource currentElement = null;
164 * The Session used to access the graph. Received from outside of this
165 * class and therefore it is not disposed here, just used.
167 private final Session session;
169 private final CopyOnWriteArrayList<HistoryListener> historyListeners = new CopyOnWriteArrayList<HistoryListener>();
171 private final AsyncRequestProcessor updater;
175 protected IWorkbenchSite site;
177 private final ChangeListener changeListener = new ChangeListener() {
179 public void graphChanged(ChangeEvent e) {
180 // This makes sure that the transaction for updating this
181 // GraphDebugger get executed in a serialized fashion.
182 updater.asyncRequest(new ReadRequest() {
185 public void run(ReadGraph graph) throws DatabaseException {
186 updateContent(graph, currentElement);
198 * @param resource the initial resource to debug or <code>null</code> for
199 * initially blank UI.
200 * @param site the workbench site that contains this debugger, for workbench
203 public GraphDebugger(Composite parent, int style, final Session session, Resource resource, IWorkbenchSite site) {
204 this(parent, style, session, resource);
212 * @param resource the initial resource to debug or <code>null</code> for
213 * initially blank UI.
215 public GraphDebugger(Composite parent, int style, final Session session, Resource resource) {
216 super(parent, style);
217 Assert.isNotNull(session, "session is null"); //$NON-NLS-1$
218 this.session = session;
219 this.currentElement = resource;
220 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
222 updater = session;//.getService(MergingGraphRequestProcessor.class);
226 addDisposeListener(new DisposeListener() {
228 public void widgetDisposed(DisposeEvent e) {
229 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
230 support.removeListener(changeListener);
236 * When given to setStatus, indicates that the message shouldn't be touched
237 * since <code>null</code> has a different meaning.
239 private static final String DONT_TOUCH = "DONT_TOUCH"; //$NON-NLS-1$
241 protected void setStatus(String message, String error) {
242 IStatusLineManager status = WorkbenchUtils.getStatusLine(site);
243 if (status != null) {
244 if (message != DONT_TOUCH)
245 status.setMessage(message);
246 if (error != DONT_TOUCH)
247 status.setErrorMessage(error);
251 public void defaultInitializeUI() {
252 setLayout(new GridLayout(2, false));
253 setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
255 createResourceText(this);
256 createDropLabel(this);
261 protected void initializeCSS() {
262 // Extract default css to a temporary location if necessary.
264 IPath absolutePath = PathUtils.getAbsolutePath(Activator.PLUGIN_ID, DEFAULT_DEBUGGER_CSS_PATH);
265 if (absolutePath != null) {
266 cssPath = absolutePath.toFile().toURI().toString();
268 File tempDir = FileUtils.getOrCreateTemporaryDirectory(false);
269 File css = new File(tempDir, DEFAULT_DEBUGGER_CSS_FILE);
271 URL url = FileLocator.find(Activator.getDefault().getBundle(), new Path(DEFAULT_DEBUGGER_CSS_PATH), null);
273 throw new FileNotFoundException("Could not find '" + DEFAULT_DEBUGGER_CSS_PATH + "' in bundle '" + Activator.PLUGIN_ID + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
274 cssPath = FileUtils.copyResource(url, css, true).toURI().toString();
276 cssPath = css.toURI().toString();
279 } catch (IOException e) {
280 // CSS extraction failed, let's just live without it then.
281 ErrorLogger.defaultLogWarning(e);
285 private static final String PROMPT_TEXT = Messages.GraphDebugger_EnterResourceIDorURI;
287 public void createResourceText(final Composite parent) {
288 final Text text = new Text(parent, SWT.BORDER);
289 text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
290 text.setText(PROMPT_TEXT);
291 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(text);
293 text.addFocusListener(new FocusListener() {
295 public void focusLost(FocusEvent e) {
296 if (text.getText().trim().equals("")) { //$NON-NLS-1$
297 text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
298 text.setText(PROMPT_TEXT);
302 public void focusGained(FocusEvent e) {
303 if (text.getText().trim().equals(PROMPT_TEXT)) {
304 text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_BLACK));
305 text.setText(""); //$NON-NLS-1$
310 text.addKeyListener(new KeyAdapter() {
312 public void keyPressed(KeyEvent e) {
313 if (e.keyCode == SWT.CR) {
314 String input = text.getText();
315 setLookupInput(input);
320 final Button button = new Button(parent, SWT.FLAT);
321 button.setText(Messages.GraphDebugger_Lookup);
322 button.setEnabled(false);
323 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(button);
325 text.addKeyListener(new KeyAdapter() {
328 public void keyPressed(KeyEvent e) {
329 if (e.keyCode == 13) {
330 String input = text.getText();
331 setLookupInput(input);
337 button.addSelectionListener(new SelectionListener() {
339 public void widgetDefaultSelected(SelectionEvent e) {
343 public void widgetSelected(SelectionEvent e) {
344 String input = text.getText();
345 setLookupInput(input);
349 text.addModifyListener(new ModifyListener() {
351 public void modifyText(ModifyEvent e) {
352 String input = text.getText().trim();
353 if (!input.equals(PROMPT_TEXT) && !input.equals("")) //$NON-NLS-1$
354 button.setEnabled(true);
356 button.setEnabled(false);
361 public void setLookupInput(String input) {
362 // There's no harm in trimming out spaces from both ends of the input.
363 input = input.trim();
365 SerialisationSupport support = session.getService(SerialisationSupport.class);
366 if (input.startsWith("$")) { //$NON-NLS-1$
368 Resource r = support.getResource(Long.parseLong(input.substring(1)));
370 } catch (NumberFormatException e1) {
371 // Ignore, may happen for crap input
372 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusInvalidPrefixedInput);
373 } catch (Exception e1) {
374 ErrorLogger.defaultLogError(e1);
375 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusResourceIDFailed);
380 String[] parts = input.split("-"); //$NON-NLS-1$
382 if (parts.length == 1) {
384 int resourceKey = Integer.parseInt(parts[0].trim());
385 Resource r = support.getResource(resourceKey);
386 // Some validation, not enough though
387 ClusteringSupport cs = session.getService(ClusteringSupport.class);
388 long cluster = cs.getCluster(r);
392 } catch (NumberFormatException e1) {
393 // Ignore, may happen for crap input
394 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusInvalidInput);
395 } catch (Exception e1) {
396 ErrorLogger.defaultLogError(e1);
397 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusTransientResourceID);
399 } else if (parts.length == 2) {
401 int resourceIndex = Integer.parseInt(parts[1]);
402 long clusterId = Long.parseLong(parts[0]);
403 ClusteringSupport cs = session.getService(ClusteringSupport.class);
404 Resource r = cs.getResourceByIndexAndCluster(resourceIndex, clusterId);
406 } catch (NumberFormatException e1) {
407 // Ignore, may happen for crap input
408 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusInvalidInputExpectIdxClusterIds);
409 } catch (Exception e1) {
410 ErrorLogger.defaultLogError(e1);
411 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusIndexLookpuFailed);
415 // Try to see if the input data is an URI reference
417 // First check that the input really is a proper URI.
419 if (!input.equals("http:/") && input.endsWith("/")) //$NON-NLS-1$ //$NON-NLS-2$
420 uri = input.substring(0, input.length() - 1);
422 Resource r = session.syncRequest( Queries.resource( uri ) );
425 } catch (URISyntaxException e) {
426 // Ignore, this is not a proper URI at all.
427 } catch (ResourceNotFoundException e1) {
428 // Ok, this was an URI, but no resource was found.
429 setStatus(DONT_TOUCH, NLS.bind( Messages.GraphDebugger_StatusResourceforURI , input ));
431 } catch (DatabaseException e1) {
432 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusURIlookupFailed);
433 ErrorLogger.defaultLogError(e1);
436 setStatus(DONT_TOUCH, Messages.GraphDebugger_StatusInvalidInputResIdORURIExpected);
439 public Label createDropLabel(Composite parent) {
440 final Label label = new Label(parent, SWT.BORDER);
441 label.setAlignment(SWT.CENTER);
442 label.setText(Messages.GraphDebugger_LabelDragResource);
443 label.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
444 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).hint(SWT.DEFAULT, 20).span(2, 1).grab(true, false).applyTo(label);
446 // Add resource id drop support to the drop-area.
447 DropTarget dropTarget = new DropTarget(label, DND.DROP_LINK | DND.DROP_COPY);
448 dropTarget.setTransfer(new Transfer[] { TextTransfer.getInstance(), ResourceReferenceTransfer.getInstance(), LocalObjectTransfer.getTransfer() });
449 dropTarget.addDropListener(new DropTargetAdapter() {
451 public void dragEnter(DropTargetEvent event) {
452 event.detail = DND.DROP_LINK;
453 label.setBackground((Color) resourceManager.get(green));
457 public void dragLeave(DropTargetEvent event) {
458 label.setBackground(null);
462 public void drop(DropTargetEvent event) {
463 label.setBackground(null);
464 ResourceArray[] data = parseEventData(event);
465 if (data == null || data.length != 1) {
466 event.detail = DND.DROP_NONE;
469 final ResourceArray array = data[0];
470 final Resource r = array.resources[array.resources.length - 1];
475 private ResourceArray[] parseEventData(DropTargetEvent event) {
476 //System.out.println("DATA: " + event.data);
477 if (event.data instanceof String) {
479 SerialisationSupport support = session.getService(SerialisationSupport.class);
480 return ResourceTransferUtils.readStringTransferable(support, (String) event.data).toResourceArrayArray();
481 } catch (IllegalArgumentException e) {
482 ErrorLogger.defaultLogError(e);
483 } catch (DatabaseException e) {
484 ErrorLogger.defaultLogError(e);
487 ResourceArray[] ret = ResourceAdaptionUtils.toResourceArrays(event.data);
497 public Browser createBrowser(Composite parent) {
499 browser = new Browser(parent, SWT.WEBKIT);
500 } catch (SWTError e) {
501 //System.out.println("Could not instantiate Browser: " + e.getMessage());
502 browser = new Browser(parent, SWT.NONE);
504 GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(browser);
506 // Left/right arrows for back/forward
507 browser.addKeyListener(new KeyAdapter() {
510 // public void keyPressed(KeyEvent e) {
511 // if (e.keyCode == SWT.F5) {
517 public void keyReleased(KeyEvent e) {
518 // System.out.println("key, char: " + e.keyCode + ", " + (int) e.character + " (" + e.character + ")");
519 if (e.keyCode == SWT.BS) {
523 if (e.keyCode == SWT.F5) {
527 if ((e.stateMask & SWT.ALT) != 0) {
528 if (e.keyCode == SWT.ARROW_RIGHT)
530 if (e.keyCode == SWT.ARROW_LEFT)
536 // Add listener for debugging functionality
537 browser.addLocationListener(new LocationAdapter() {
539 public void changing(LocationEvent event) {
540 String location = event.location;
541 if (location.startsWith("simantics:browser")) //$NON-NLS-1$
542 location = "about:" + location.substring(17); //$NON-NLS-1$
543 //System.out.println("changing: location=" + location);
545 // Do not follow links that are meant as actions that are
548 if ("about:blank".equals(location)) { //$NON-NLS-1$
549 // Just changing to the same old blank url is ok since it
550 // allows the browser to refresh itself.
554 if (location.startsWith("about:-link")) { //$NON-NLS-1$
555 String target = location.replace("about:-link", ""); //$NON-NLS-1$ //$NON-NLS-2$
556 Resource element = links.getRight(target);
557 if (element == currentElement) {
561 changeLocation(element);
562 } else if (location.startsWith("about:-remove")) { //$NON-NLS-1$
563 String target = location.replace("about:-remove", ""); //$NON-NLS-1$ //$NON-NLS-2$
564 String n[] = target.split(STATEMENT_PART_SEPARATOR);
568 final Resource s = links.getRight(n[0]);
569 final Resource p = links.getRight(n[1]);
570 final Resource o = links.getRight(n[2]);
572 // Make sure this is what the use wants.
573 MessageDialog md = new MessageDialog(
575 Messages.GraphDebugger_ConfirmActionsDots,
577 Messages.GraphDebugger_ConfirmActionsDotsMsg,
578 MessageDialog.QUESTION, new String[] { Messages.GraphDebugger_Cancel, Messages.GraphDebugger_Continue }, 0);
579 if (md.open() != 1) {
583 session.asyncRequest(new WriteRequest() {
586 public void perform(WriteGraph g) throws DatabaseException {
588 List<Resource> ls = OrderedSetUtils.toList(g, s);
590 OrderedSetUtils.remove(g, s, o);
591 } catch (DatabaseException e) {
595 List<Resource> ls = ListUtils.toList(g, s);
597 ListUtils.removeElement(g, s, o);
598 } catch (DatabaseException e) {
601 g.denyStatement(s, p, o);
604 }, parameter -> refreshBrowser()
606 } else if (location.startsWith("about:-edit-value")) { //$NON-NLS-1$
607 String target = location.replace("about:-edit-value", ""); //$NON-NLS-1$ //$NON-NLS-2$
608 final Resource o = links.getRight(target);
610 session.asyncRequest(new ReadRequest() {
612 String previousValue;
615 public void run(ReadGraph graph) throws DatabaseException {
617 previousValue = getResourceName(graph, o);
618 final StringModifier modifier = graph.adapt(o, StringModifier.class);
619 getDisplay().asyncExec(new Runnable() {
622 InputDialog dialog = new InputDialog(
624 Messages.GraphDebugger_EditValue,
627 new IInputValidator() {
629 public String isValid(String newText) {
630 return modifier.isValid(newText);
633 private static final String DIALOG = "DebuggerEditValueDialog"; //$NON-NLS-1$
634 private IDialogSettings dialogBoundsSettings;
636 protected IDialogSettings getDialogBoundsSettings() {
637 if (dialogBoundsSettings == null) {
638 IDialogSettings settings = Activator.getDefault().getDialogSettings();
639 dialogBoundsSettings = settings.getSection(DIALOG);
640 if (dialogBoundsSettings == null)
641 dialogBoundsSettings = settings.addNewSection(DIALOG);
643 return dialogBoundsSettings;
646 protected Point getInitialSize() {
647 Point defaultSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
648 Point result = super.getInitialSize();
649 if (defaultSize.equals(result))
650 return new Point(600, 400);
653 protected int getShellStyle() {
654 return super.getShellStyle() | SWT.RESIZE;
656 protected org.eclipse.swt.widgets.Control createDialogArea(Composite parent) {
657 Composite composite = (Composite) super.createDialogArea(parent);
658 getText().setLayoutData(
659 new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL
660 | GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
662 Label label = new Label(composite, SWT.NONE);
663 label.moveAbove(getText());
664 label.setText(Messages.GraphDebugger_InputNewPropertyValue);
665 GridData data = new GridData(GridData.GRAB_HORIZONTAL
666 | GridData.HORIZONTAL_ALIGN_FILL
667 | GridData.VERTICAL_ALIGN_CENTER);
668 data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
669 label.setLayoutData(data);
670 label.setFont(parent.getFont());
674 protected int getInputTextStyle() {
675 return SWT.MULTI | SWT.BORDER;
678 int ok = dialog.open();
682 final String value = dialog.getValue();
683 session.asyncRequest(new WriteRequest() {
685 public void perform(WriteGraph g) throws DatabaseException {
686 //modifier.modify( g, htmlEscape( value ) );
687 modifier.modify( g, value );
690 if (parameter != null)
691 ErrorLogger.defaultLogError(parameter);
699 }, new ProcedureAdapter<Object>() {
701 public void exception(Throwable t) {
702 ErrorLogger.defaultLogError(t);
710 // Schedule a request that updates the browser content.
712 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
713 support.removeListener(changeListener);
718 public void refreshBrowser() {
719 if (currentElement == null)
722 // Schedule a request that updates the browser content.
723 updater.asyncRequest(new ReadRequest() {
726 public void run(ReadGraph graph) throws DatabaseException {
727 updateContent(graph, currentElement);
734 public Resource getDebuggerLocation() {
735 return currentElement;
738 public void changeLocation(Resource element) {
739 if (currentElement != null) {
740 backHistory.addLast(currentElement);
742 currentElement = element;
743 forwardHistory.clear();
746 setStatus(DONT_TOUCH, null);
747 fireHistoryChanged();
750 public void addHistoryListener(HistoryListener l) {
751 historyListeners.add(l);
754 public void removeHistoryListener(HistoryListener l) {
755 historyListeners.remove(l);
758 private void fireHistoryChanged() {
759 for (HistoryListener l : historyListeners)
763 public boolean hasBackHistory() {
764 return backHistory.isEmpty();
767 public boolean hasForwardHistory() {
768 return forwardHistory.isEmpty();
772 if (backHistory.isEmpty())
775 forwardHistory.addFirst(currentElement);
776 currentElement = backHistory.removeLast();
779 fireHistoryChanged();
782 public void forward() {
783 if (forwardHistory.isEmpty())
786 backHistory.addLast(currentElement);
787 currentElement = forwardHistory.removeFirst();
790 fireHistoryChanged();
793 protected String toName(Object o) {
794 Class<?> clazz = o.getClass();
795 if (clazz.isArray()) {
796 int length = Array.getLength(o);
797 if (length > RESOURCE_NAME_MAX_LENGTH) {
798 if (o instanceof byte[]) {
799 byte[] arr = (byte[]) o;
800 byte[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
801 return truncated("byte", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
802 } else if (o instanceof int[]) {
803 int[] arr = (int[]) o;
804 int[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
805 return truncated("int", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
806 } else if (o instanceof long[]) {
807 long[] arr = (long[]) o;
808 long[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
809 return truncated("long", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
810 } else if (o instanceof float[]) {
811 float[] arr = (float[]) o;
812 float[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
813 return truncated("float", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
814 } else if (o instanceof double[]) {
815 double[] arr = (double[]) o;
816 double[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
817 return truncated("double", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
818 } else if (o instanceof boolean[]) {
819 boolean[] arr = (boolean[]) o;
820 boolean[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
821 return truncated("boolean", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
822 } else if (o instanceof Object[]) {
823 Object[] arr = (Object[]) o;
824 Object[] arr2 = Arrays.copyOf(arr, RESOURCE_NAME_MAX_LENGTH);
825 return truncated("Object", Arrays.toString(arr2), arr.length); //$NON-NLS-1$
827 return "Unknown big array " + o.getClass(); //$NON-NLS-1$
830 return o.getClass().getComponentType() + "[" + length + "] = " + ObjectUtils.toString(o); //$NON-NLS-1$ //$NON-NLS-2$
836 protected String truncated(String type, String string, int originalLength) {
837 return type + "[" + RESOURCE_NAME_MAX_LENGTH + "/" + originalLength + "] = " + string; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
840 public static String htmlEscape(String s)
842 return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "<br/>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
846 * Get resource name(?)
852 protected String getResourceName(ReadGraph graph, Resource r) {
856 //System.out.println("hasValue(" + NameUtils.getSafeName(graph, r, true));
857 if (graph.hasValue(r)) {
858 // too large array may cause application to run out of memory.
859 //System.out.println("getValue(" + NameUtils.getSafeName(graph, r, true));
860 Datatype type = graph.getPossibleRelatedValue(r, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
862 Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class );
863 if (type.equals( rviBinding.type() )) {
864 RVI rvi = graph.getValue(r, rviBinding);
867 Variable v = Variables.getConfigurationContext( graph, currentElement );
868 name = rvi.asString(graph, v);
869 // name = rvi.resolve(graph, v).getURI(graph);
870 } catch (DatabaseException dbe ) {
871 name = rvi.toString( graph );
874 long valueSize = NameUtils.getPossibleValueSize(graph, r);
875 if (valueSize > RESOURCE_NAME_MAX_LENGTH) {
876 // Binding b = Bindings.getBinding(type);
877 // Object v = graph.getValue(r, b);
878 // Serializer s = Bindings.getSerializerUnchecked(b);
879 // int size = s.getSize(v);
880 name = "Approx. " + valueSize + " byte literal of type " + type.toSingleLineString(); //$NON-NLS-1$ //$NON-NLS-2$
882 Binding b = Bindings.getBinding(type);
883 Object v = graph.getValue(r, b);
884 if (b.type() instanceof StringType) {
885 name = (String) graph.getValue(r, b);
887 name = b.toString(v, false);
889 if (type instanceof ArrayType){
890 name = name.substring(1, name.length()-1);
895 Object o = graph.getValue(r);
900 name = "<empty value>"; //$NON-NLS-1$
904 // Does resource have a file ??
907 // Accessor accessor = graph.getAccessor(r);
908 // name = "File of type " + accessor.type().toSingleLineString();
909 // } catch (DatabaseException e) {
910 // // No file, try next alternative.
914 //name = graph.adapt(r, String.class);
916 name = DebugUtils.getSafeLabel(graph, r);
918 name = "<empty name>"; //$NON-NLS-1$
920 // ClusteringSupport support = graph.getSession().getService(ClusteringSupport.class);
922 // return "[" + r.getResourceId() + " - " + support.getCluster(r) + "]";
923 if(displayClusters) {
924 // SessionDebug debug = graph.getSession().getDebug();
925 // name += " (" + debug.getCluster(r) + ")";
928 } catch (AdaptionException e) {
929 // e.printStackTrace();
930 String name = safeReadableString(graph, r);
932 // MessageService.defaultLog(new DetailStatus(IDetailStatus.DEBUG, Activator.PLUGIN_ID, 0, NLS.bind(Messages.Name_adaption_problem, MessageUtil.resource(session, r, "this resource")), e));
933 // } catch (ReferenceSerializationException e1) {
934 // e1.printStackTrace();
935 // ErrorLogger.defaultLogWarning(e1);
938 } catch (Exception e) {
939 ErrorLogger.defaultLogError(e);
940 String name = safeReadableString(graph, r);
942 // MessageService.defaultLog(new DetailStatus(IDetailStatus.DEBUG, Activator.PLUGIN_ID, 0, NLS.bind(Messages.Name_formulation_problem, MessageUtil.resource(session, r, "this resource")), e));
943 // } catch (ReferenceSerializationException e1) {
944 // e1.printStackTrace();
945 // ErrorLogger.defaultLogWarning(e1);
951 private String getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {
954 Layer0 L0 = Layer0.getInstance(graph);
955 if (graph.isInstanceOf(r, L0.Assertion)) {
956 Resource pred = graph.getPossibleObject(r, L0.HasPredicate);
957 // Don't know how I encountered this but it seems to be possible in some cases..
958 // Resource obj = graph.getSingleObject(r, L0.HasObject);
959 Resource obj = graph.getPossibleObject(r, L0.HasObject);
960 String tmp = htmlEscape( (pred == null ? "No predicate ?" : getResourceName(graph, pred)) + " -> " + (obj == null ? "No object ?" : getResourceName(graph, obj)) + " (Assertion)" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
961 name = tmp.substring(0, Math.min(80, tmp.length()));
963 String resourceName = getResourceName(graph, r);
964 if(resourceName.equals("Inverse")) { //$NON-NLS-1$
965 Resource inverse = graph.getPossibleInverse(r);
966 if(inverse != null && graph.hasStatement(inverse, L0.ConsistsOf, r))
967 resourceName = getResourceName(graph, inverse) + "/Inverse"; //$NON-NLS-1$
969 String tmp = htmlEscape( resourceName );
970 name = tmp.substring(0, Math.min(80, tmp.length()));
973 } catch (OutOfMemoryError e) {
974 name = "OutOfMemoryError"; //$NON-NLS-1$
976 String ret = "<a href=\"simantics:browser-link" + getLinkString(r) + "\">" //$NON-NLS-1$ //$NON-NLS-2$
978 + "</a>"; //$NON-NLS-1$
979 if (graph.isInstanceOf(r, L0.Literal)) {
980 ret += " <a class=\"edit-link\" href=\"simantics:browser-edit-value" + getLinkString(r) + "\">" //$NON-NLS-1$ //$NON-NLS-2$
981 + "(edit)" //$NON-NLS-1$
982 + "</a>"; //$NON-NLS-1$
987 private String getStatementRemoveRef(Resource s, Resource p, Resource o) {
988 return "<a href=\"simantics:browser-remove" + getStatementString(s, p, o) //$NON-NLS-1$
989 + "\" title=\"Remove this statement\">X</a>"; //$NON-NLS-1$
992 private void updatePred(StringBuffer content, ReadGraph graph, Resource subj, Resource pred, List<Resource[]> stats) throws DatabaseException {
993 // Generate output content from statements
994 String[][] objects = new String[stats.size()][];
995 for (int i = 0; i < stats.size(); ++i) {
996 Resource stmSubject = stats.get(i)[0];
997 Resource object = stats.get(i)[1];
999 objects[i] = new String[4];
1000 objects[i][0] = getLinkString(object);
1001 objects[i][1] = htmlEscape( getResourceName(graph, object) );
1002 objects[i][2] = getResourceRef(graph, object);
1004 // Make a note if the statement was acquired.
1005 if(!stmSubject.equals(subj)) {
1006 objects[i][3] = " (in " + getResourceRef(graph, stmSubject) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
1010 // Sort statements by object name
1011 Arrays.sort(objects, new Comparator<String[]>() {
1013 public int compare(String[] o1, String[] o2) {
1014 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1[1], o2[1]);
1018 // Output table rows
1019 for (int i = 0; i < objects.length; ++i) {
1020 content.append("<tr>"); //$NON-NLS-1$
1023 content.append("<td rowspan=\"").append(objects.length).append("\" valign=\"top\">").append(getResourceRef(graph, pred)).append("</td>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1026 if (objects[i][3] == null) content.append("<td>"); //$NON-NLS-1$
1027 else content.append("<td class=\"acquired\">"); //$NON-NLS-1$
1029 content.append(objects[i][2]);
1030 if (objects[i][3] != null)
1031 content.append(objects[i][3]);
1033 content.append("</td>"); //$NON-NLS-1$
1035 VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);
1036 VirtualGraph vg = vgs.getGraph(graph, subj, pred, links.getRight(objects[i][0]));
1039 content.append("<td>").append(vg.toString()).append("</td>"); //$NON-NLS-1$ //$NON-NLS-2$
1041 content.append("<td>DB</td>"); //$NON-NLS-1$
1045 // Statement remove -link column
1046 // Only allowed for non-acquired statements.
1047 if (objects[i][3] == null) {
1048 content.append("<td class=\"remove\">"); //$NON-NLS-1$
1049 content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));
1050 content.append("</td>"); //$NON-NLS-1$
1052 content.append("</tr>"); //$NON-NLS-1$
1056 private void updateTag(StringBuffer content, ReadGraph graph, Resource subj, Resource tag) throws DatabaseException {
1058 // Generate output content from statements
1059 String ref = getResourceRef(graph, tag);
1061 content.append("<tr>"); //$NON-NLS-1$
1062 content.append("<td rowspan=\"1\" colspan=\"3\" valign=\"top\">").append(ref).append("</td>"); //$NON-NLS-1$ //$NON-NLS-2$
1063 //content.append("<td>" + name + "</td>");
1064 content.append("<td class=\"remove\">"); //$NON-NLS-1$
1065 content.append(getStatementRemoveRef(subj, tag, subj));
1066 content.append("</td>"); //$NON-NLS-1$
1067 content.append("</tr>"); //$NON-NLS-1$
1071 private void updateOrderedSet(StringBuffer content, ReadGraph graph, Resource subj) throws DatabaseException {
1072 //List<Resource> list = OrderedSetUtils.toList(graph, subj);
1074 // Generate output content from statements
1075 String[][] objects = new String[stats.size()][];
1076 for (int i = 0; i < stats.size(); ++i) {
1077 Resource stmSubject = stats.get(i)[0];
1078 Resource object = stats.get(i)[1];
1080 objects[i] = new String[4];
1081 objects[i][0] = getLinkString(object);
1082 objects[i][1] = getResourceName(graph, object);
1083 objects[i][2] = getResourceRef(graph, object);
1085 // Make a note if the statement was acquired.
1086 if(!stmSubject.equals(subj)) {
1087 objects[i][3] = " (acquired from " + getResourceRef(graph, stmSubject) + ")";
1091 // Sort statements by object name
1092 Arrays.sort(objects, new Comparator<String[]>() {
1094 public int compare(String[] o1, String[] o2) {
1095 return o1[1].compareTo(o2[1]);
1099 List<String> list = new ArrayList<String>();
1100 Resource cur = subj;
1103 cur = OrderedSetUtils.next(graph, subj, cur);
1104 } catch(DatabaseException e) {
1105 list.add("<span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">" + e.getMessage() + "</span>"); //$NON-NLS-1$ //$NON-NLS-2$
1106 Resource inv = graph.getPossibleInverse(subj);
1107 for(Statement stat : graph.getStatements(cur, L0.IsRelatedTo)) {
1108 if(stat.getSubject().equals(cur)) {
1109 if(stat.getPredicate().equals(subj)) {
1110 list.add("next " + getResourceRef(graph, stat.getObject())); //$NON-NLS-1$
1112 else if(stat.getPredicate().equals(inv)) {
1113 list.add("prev " + getResourceRef(graph, stat.getObject())); //$NON-NLS-1$
1119 if(cur.equals(subj))
1121 list.add(getResourceRef(graph, cur));
1124 // Output table rows
1125 for (int i = 0; i < list.size() ; ++i) {
1126 content.append("<tr>"); //$NON-NLS-1$
1129 content.append("<td rowspan=\"").append(list.size()).append("\" valign=\"top\">Ordered Set Elements</td>"); //$NON-NLS-1$ //$NON-NLS-2$
1132 content.append("<td>"); //$NON-NLS-1$
1133 content.append(list.get(i));
1134 content.append("</td>"); //$NON-NLS-1$
1136 // Statement remove -link column
1137 // Only allowed for non-acquired statements.
1138 /*if (objects[i][3] == null) {
1139 content.append("<td class=\"remove\">");
1140 content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));
1141 content.append("</td>");
1143 content.append("</tr>"); //$NON-NLS-1$
1147 private void updateLinkedList(StringBuffer content, ReadGraph graph, Resource subj) throws DatabaseException {
1149 List<String> list = new ArrayList<String>();
1152 List<Resource> resources = ListUtils.toList(graph, subj);
1153 for(Resource element : resources) {
1154 list.add(getResourceRef(graph, element));
1156 } catch (DatabaseException e) {
1157 throw new ValidationException(e);
1160 // Output table rows
1161 for (int i = 0; i < list.size() ; ++i) {
1162 content.append("<tr>"); //$NON-NLS-1$
1165 content.append("<td rowspan=\"").append(list.size()).append("\" valign=\"top\">Linked List Elements</td>"); //$NON-NLS-1$ //$NON-NLS-2$
1168 content.append("<td>"); //$NON-NLS-1$
1169 content.append(list.get(i));
1170 content.append("</td><td>DB</td>"); //$NON-NLS-1$
1172 // Statement remove -link column
1173 // Only allowed for non-acquired statements.
1174 /*if (objects[i][3] == null) {
1175 content.append("<td class=\"remove\">");
1176 content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));
1177 content.append("</td>");
1179 content.append("</tr>"); //$NON-NLS-1$
1183 protected synchronized void updateContent(final ReadGraph graph, Resource... resources) throws DatabaseException {
1184 L0 = Layer0.getInstance(graph);
1187 StringBuffer content = new StringBuffer();
1189 // Generate HTML -page
1190 content.append("<html>\n<head>\n") //$NON-NLS-1$
1192 .append("\n</head>\n") //$NON-NLS-1$
1193 .append("<body>\n") //$NON-NLS-1$
1194 .append("<div id=\"mainContent\">\n\n"); //$NON-NLS-1$
1196 for (Resource r : resources) {
1202 uri = graph.syncRequest(new ResourceToPossibleURI(r));
1203 } catch (Exception e) {
1204 ErrorLogger.defaultLogError(e);
1205 uri = "Cannot get URI: " + e.getMessage(); //$NON-NLS-1$
1209 content.append("<div id=\"top\">\n"); //$NON-NLS-1$
1210 content.append("<table class=\"top\">\n"); //$NON-NLS-1$
1212 content.append("<tr><td class=\"top_key\">URI</td><td class=\"top_value\"><span id=\"uri\">").append(uri).append("</span></td></tr>\n"); //$NON-NLS-1$ //$NON-NLS-2$
1215 XSupport xs = graph.getService(XSupport.class);
1216 boolean immutable = xs.getImmutable(r);
1218 Collection<Statement> statements = graph.getStatements(r, L0.IsWeaklyRelatedTo);
1219 HashMultiMap<Resource, Resource[]> map = new HashMultiMap<Resource, Resource[]>();
1220 for(org.simantics.db.Statement statement : statements) {
1221 Resource predicate = null;
1222 Resource subject = null;
1223 Resource obj = null;
1225 predicate = statement.getPredicate();
1226 subject = statement.getSubject();
1227 obj = statement.getObject();
1228 map.add(predicate, new Resource[] {subject, obj});
1229 } catch (Throwable e) {
1230 ErrorLogger.defaultLogError("Cannot find statement " + subject + " " + predicate + " " + obj, e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1233 SerialisationSupport ss = graph.getSession().getService(SerialisationSupport.class);
1234 ClusteringSupport support = graph.getSession().getService(ClusteringSupport.class);
1235 content.append("<tr><td class=\"top_key\">Identifiers</td><td class=\"top_value\">"); //$NON-NLS-1$
1236 content.append("<span id=\"resource_id\">") //$NON-NLS-1$
1237 .append(" RID = $").append(r.getResourceId()) //$NON-NLS-1$
1238 .append(" Resource Key = ").append(ss.getTransientId(r)) //$NON-NLS-1$
1239 .append(" CID = ").append(support.getCluster(r)); //$NON-NLS-1$
1240 content.append("</span></td>"); //$NON-NLS-1$
1242 content.append("<td class=\"remove\">[IMMUTABLE]</td>"); //$NON-NLS-1$
1243 content.append("</tr>\n"); //$NON-NLS-1$
1245 boolean isClusterSet = support.isClusterSet(r);
1246 Resource parentSet = support.getClusterSetOfCluster(r);
1247 String parentSetURI = parentSet != null ? graph.getPossibleURI(parentSet) : null;
1249 content.append("<tr><td class=\"top_key\">Clustering</td><td class=\"top_value\">"); //$NON-NLS-1$
1250 content.append("<span id=\"resource_id\">"); //$NON-NLS-1$
1252 if(parentSetURI != null)
1253 content.append(" Containing cluster set = ").append(parentSetURI); //$NON-NLS-1$
1254 else if (parentSet != null)
1255 content.append(" Containing cluster set = ").append(parentSet.toString()); //$NON-NLS-1$
1257 content.append(" Not in any cluster set "); //$NON-NLS-1$
1259 content.append("</span></td>"); //$NON-NLS-1$
1261 content.append("<td class=\"remove\">[CLUSTER SET]</td>"); //$NON-NLS-1$
1262 content.append("</tr>\n"); //$NON-NLS-1$
1264 // If the resource has a value, show it.
1265 String resourceValue = getResourceValue(graph, r);
1266 if (resourceValue != null) {
1268 .append("<tr><td class=\"top_key\">Attached value</td><td class=\"top_value\">") //$NON-NLS-1$
1269 .append(htmlEscape(resourceValue))
1270 .append("</td></tr>\n"); //$NON-NLS-1$
1274 content.append("</table>\n"); //$NON-NLS-1$
1275 content.append("</div>\n"); //$NON-NLS-1$
1277 content.append("\n<div id=\"data\">\n"); //$NON-NLS-1$
1278 content.append("<table>\n") //$NON-NLS-1$
1279 .append("<tr><th>Predicate</th><th>Object</th><th>Graph</th></tr>") //$NON-NLS-1$
1280 .append("<tr><td class=\"subtitle\" colspan=\"3\">Basic information</td></tr>"); //$NON-NLS-1$
1282 boolean isOrderedSet = graph.isInstanceOf(r, L0.OrderedSet);
1283 boolean isLinkedList = graph.isInstanceOf(r, L0.List);
1286 // BASIC INFORMATION:
1287 for (Resource pred :
1288 new Resource[] {L0.HasName, L0.InstanceOf,
1289 L0.Inherits, L0.SubrelationOf,
1290 L0.PartOf, L0.ConsistsOf})
1291 if (map.containsKey(pred))
1292 updatePred(content, graph, r, pred, map.remove(pred));
1295 content.append("<tr><td class=\"subtitle\" colspan=\"3\">Tags</td></tr>"); //$NON-NLS-1$
1296 for(Statement stm : statements) {
1297 if(stm.getSubject().equals(stm.getObject())) {
1298 updateTag(content, graph, r, stm.getPredicate());
1299 map.remove(stm.getPredicate());
1304 content.append("<tr><td class=\"subtitle\" colspan=\"3\">Ordered Sets</td></tr>"); //$NON-NLS-1$
1305 for(Statement stm : statements) {
1306 Resource predicate = stm.getPredicate();
1307 if(graph.isInstanceOf(stm.getPredicate(), L0.OrderedSet)) {
1308 updateTag(content, graph, r, stm.getPredicate());
1309 if(map.get(stm.getPredicate()) != null && map.get(stm.getPredicate()).size() == 1)
1310 map.remove(stm.getPredicate());
1312 Resource inverse = graph.getPossibleInverse(predicate);
1313 if (inverse != null) {
1314 if(graph.isInstanceOf(inverse, L0.OrderedSet)) {
1315 if(map.get(stm.getPredicate()) != null && map.get(stm.getPredicate()).size() == 1)
1316 map.remove(stm.getPredicate());
1319 // FIXME : should we infor missing inverse
1324 content.append("<tr><td class=\"subtitle\" colspan=\"3\">Is Related To</td></tr>"); //$NON-NLS-1$
1326 // ELEMENTS OF ORDERED SET
1328 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");
1330 updateOrderedSet(content, graph, r);
1331 } catch (ValidationException e) {
1332 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>"); //$NON-NLS-1$ //$NON-NLS-2$
1336 // ELEMENTS OF LINKED LIST
1338 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");
1340 updateLinkedList(content, graph, r);
1341 } catch (ValidationException e) {
1342 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>"); //$NON-NLS-1$ //$NON-NLS-2$
1346 // IS RELATED TO (other)
1347 Resource[] preds = map.keySet().toArray(new Resource[0]);
1348 final Map<Resource, String> strmap = new HashMap<Resource, String>(preds.length);
1349 for(Resource pred : preds) {
1350 String str = htmlEscape( getResourceName(graph, pred) );
1352 str = "<null>"; //$NON-NLS-1$
1353 strmap.put(pred, str);
1355 Arrays.sort(preds, new Comparator<Resource>() {
1357 public int compare(Resource o1, Resource o2) {
1358 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(strmap.get(o1), strmap.get(o2));
1361 for(Resource pred : preds)
1362 if(graph.isSubrelationOf(pred, L0.IsRelatedTo))
1363 updatePred(content, graph, r, pred, map.get(pred));
1366 content.append("<tr><td class=\"subtitle\" colspan=\"3\">Other statements</td></tr>"); //$NON-NLS-1$
1367 for(Resource pred : preds)
1368 if(!graph.isSubrelationOf(pred, L0.IsRelatedTo))
1369 updatePred(content, graph, r, pred, map.get(pred));
1370 content.append("</table>\n"); //$NON-NLS-1$
1373 content.append("</div>\n\n"); //$NON-NLS-1$
1374 // Close #mainContent
1375 content.append("</div>\n"); //$NON-NLS-1$
1376 content.append("</body>\n</html>\n"); //$NON-NLS-1$
1379 final String finalContent = content.toString();
1380 if (!isDisposed()) {
1381 getDisplay().asyncExec(new Runnable() {
1384 if (!browser.isDisposed())
1385 browser.setText(finalContent);
1391 private String getResourceValue(ReadGraph graph, Resource r) {
1393 if (graph.hasValue(r)) {
1394 // too large array may cause application to run out of memory.
1395 //System.out.println("getValue(" + NameUtils.getSafeName(graph, r, true));
1396 Datatype type = graph.getPossibleRelatedValue(r, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
1398 Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class );
1399 if (type.equals( rviBinding.type() )) {
1400 RVI rvi = graph.getValue(r, rviBinding);
1402 Variable v = Variables.getConfigurationContext( graph, r );
1403 return rvi.asString(graph, v);
1404 } catch (DatabaseException dbe ) {
1405 return rvi.toString( graph );
1408 Binding b = Bindings.getBinding(type);
1409 Object v = graph.getValue(r, b);
1410 Serializer s = Bindings.getSerializerUnchecked(b);
1411 int size = s.getSize(v);
1412 if (size > RESOURCE_VALUE_MAX_SIZE) {
1413 return "Approx. " + size + " byte literal of type " + type.toSingleLineString(); //$NON-NLS-1$ //$NON-NLS-2$
1415 return b.toString(v, false);
1419 Object o = graph.getValue(r);
1424 } catch (DatabaseException e) {
1425 return e.getMessage();
1426 } catch (IOException e) {
1427 return e.getMessage();
1428 } catch (BindingException e) {
1429 return e.getMessage();
1433 private static String safeReadableString(ReadGraph g, Resource r) {
1435 return NameUtils.getSafeName(g, r);
1436 } catch(Throwable throwable) {
1437 ErrorLogger.defaultLogError(throwable);
1438 return "<font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1442 // private static String safeReadableString(IEntity t) {
1444 // return ResourceDebugUtils.getReadableNameForEntity(t);
1445 // } catch(Throwable throwable) {
1446 // throwable.printStackTrace();
1447 // return "<font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font>";
1451 private String getLinkString(Container<Resource> t) {
1452 String link = links.getLeft(t.get());
1454 link = UUID.randomUUID().toString();
1455 links.map(link, t.get());
1460 // private String getPropertyEditString(Container<Resource> t) {
1461 // String link = links.getLeft(t.get());
1462 // if(link == null) {
1463 // link = UUID.randomUUID().toString();
1464 // links.map(link, t.get());
1469 private String getStatementString(Resource _s, Resource _p, Resource _o) {
1470 String s = getLinkString(_s);
1471 String p = getLinkString(_p);
1472 String o = getLinkString(_o);
1473 return s + STATEMENT_PART_SEPARATOR + p + STATEMENT_PART_SEPARATOR + o;
1476 // private String getStatementString(Statement stm) {
1477 // String s = getLinkString(stm.getSubject());
1478 // String p = getLinkString(stm.getPredicate());
1479 // String o = getLinkString(stm.getObject());
1480 // return s + STATEMENT_PART_SEPARATOR + p + STATEMENT_PART_SEPARATOR + o;
1483 // private String toOutgoingTableRow(IEntity subject, Statement stm) {
1484 // boolean isAcquired = !subject.equals(stm.getSubject());
1486 // String plainCell = "%s";
1487 // String hrefCell = "<a href=\"%s\">%s</a>";
1488 // String formatTemplate = isAcquired
1489 // ? "<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>"
1490 // : "<tr>\n<td>{1}</td>\n<td>{1}</td>\n<td>{1}</td>\n<td>{1}</td>\n</tr>";
1491 // String format = NLS.bind(formatTemplate, plainCell, hrefCell);
1492 //// System.out.println("format: " + format);
1494 // IEntity s = stm.getSubject();
1495 // IEntity p = stm.getPredicate();
1496 // IEntity o = stm.getObject();
1498 //// String timePart = "[" + timeToString(t.getBegin()) + ", " + timeToString(t.getEnd()) + "]";
1501 // return !isAcquired
1502 // ? String.format(format,
1503 // "about:blank-remove" + getStatementString(stm), "Remove",
1504 // "about:blank-link" + getLinkString(s), safeReadableString(s),
1505 // "about:blank-link" + getLinkString(p), safeReadableString(p),
1506 // "about:blank-link" + getLinkString(o), safeReadableString(o))
1507 // : String.format(format,
1509 // "about:blank-link" + getLinkString(s), safeReadableString(s),
1510 // "about:blank-link" + getLinkString(p), safeReadableString(p),
1511 // "about:blank-link" + getLinkString(o), safeReadableString(o)
1513 // } catch (Throwable throwable) {
1514 // return "<tr><td colspan=\"4\"><font color=\"red\"><i>"+throwable.getClass().getName()+"</i> "+throwable.getMessage()+"</font></td></tr>";
1518 // private String intervalToString(Interval time) {
1519 // return timeToString(time.getBegin()) + ", " + timeToString(time.getEnd());
1522 // DateFormat dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
1524 // private String timeToString(long time) {
1525 // if (time == Long.MIN_VALUE)
1526 // return "-∞";
1527 // if (time == Long.MAX_VALUE)
1528 // return "+∞";
1529 // //return String.valueOf(time);
1530 // Date d = new Date(time);
1531 // return dateTimeFormat.format(d);
1534 private String getHead() {
1535 String result = ""; //$NON-NLS-1$
1536 if (cssPath != null) {
1537 result = "<link href=\"" + cssPath + "\" rel=\"stylesheet\" type=\"text/css\">"; //$NON-NLS-1$ //$NON-NLS-2$