package org.simantics.team.ui; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.graphics.Image; import org.simantics.db.ChangeSet; import org.simantics.db.ChangeSetIdentifier; import org.simantics.db.Metadata; import org.simantics.db.ReadGraph; import org.simantics.db.Session; import org.simantics.db.common.CommentMetadata; import org.simantics.db.common.CommitMetadata; import org.simantics.db.common.UndoMetadata; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.service.ManagementSupport; import org.simantics.db.service.UndoRedoSupport; import org.simantics.team.internal.Images; import org.simantics.utils.ui.SWTUtils; public class Common { } abstract class TreeElement { abstract boolean hasChildren(); abstract Object[] getChildren(); abstract Image getIdImage(); abstract String getIdText(); final Image getDateImage() { return null; } String getDateText() { return null; } final Image getCommentImage() { return null; } String getCommentText() { return null; } } class StringElement extends TreeElement { protected final String name; protected final String value; StringElement(String name, String value) { this.name = name; this.value = value; } @Override protected Image getIdImage() { return Images.getInstance().STRING_IMAGE; } @Override protected String getIdText() { String s = toString(); return s.substring(0, Math.min(40, s.length())); } public String toString() { return name + "=" + value; } @Override boolean hasChildren() { return false; } @Override Object[] getChildren() { return new Object[0]; } } class CommentStringElement extends StringElement { CommentStringElement(String name, String value) { super(name, value); } @Override protected String getIdText() { return name.substring(0, Math.min(40, name.length())); } @Override protected String getCommentText() { return value; } } class DisplayElement extends TreeElement { protected final String name; protected final String value; DisplayElement(String name, String value) { this.name = name; this.value = value; } @Override public String toString() { return name + "=" + value; } @Override protected Image getIdImage() { return Images.getInstance().DISPLAY_IMAGE; } @Override protected String getIdText() { return name; } @Override boolean hasChildren() { return false; } @Override Object[] getChildren() { return new Object[0]; } String getValue() { return value; } } class ChangeSetDisplayElement extends DisplayElement { private final Session session; private final long csid; private String lazyValue; ChangeSetDisplayElement(String name, Session session, long csid) { super(name, ""); this.session = session; this.csid = csid; } @Override public String toString() { return name + "=" + getValue(); } @Override String getValue() { if (null == lazyValue) lazyValue = fetchChangeSet(); return lazyValue; } private String fetchChangeSet() { try { Collection css = session.sync(new UniqueRead>() { @Override public Collection perform(ReadGraph graph) throws DatabaseException { ManagementSupport ms = graph.getService(ManagementSupport.class); return ms.fetchChangeSets(graph, csid, csid); } }); if (css.size() != 1) return ""; return css.iterator().next().toString(); } catch (DatabaseException e) { Logger.defaultLogError(e); } return ""; } } class ChangeSetElement extends TreeElement implements Command { private boolean DEBUG = false; private ChangeSetIdentifier cs; private Map metadata = null; private Session session; ChangeSetElement(Session session, long csid) { this.session = session; this.cs = getChangeSetIdentifier(csid); } ChangeSetElement(Session session, ChangeSetIdentifier cs) { this.cs = cs; this.session = session; } @SuppressWarnings("unchecked") static T getMetadata(Session session, Map data, Class dataClass) { if (null == session || null == data || null == dataClass) return null; T result = null; try { Method m = dataClass.getMethod("deserialise", Session.class, byte[].class); byte[] bytes = data.get(dataClass.getName()); if (null != bytes) { Object value = m.invoke(null, session, bytes); result = (T)value; } } catch (RuntimeException e) { Logger.defaultLogError(e); } catch (Exception e) { Logger.defaultLogError(e); } return result; } @Override public void dumpToSelectedRevision() throws DatabaseException { if (null == cs) return; ManagementSupport ms = session.getService(ManagementSupport.class); long csid = cs.getId(); ms.dumpRevision(csid); if (DEBUG) System.out.println("DEBUG: Dumped change set=" + csid + "."); } @Override public void undoToSelectedRevision() throws DatabaseException { if (null == cs) return; UndoRedoSupport us = session.getService(UndoRedoSupport.class); int n = us.undoTo(session, cs.getId()); if (DEBUG) System.out.println("DEBUG: Reverted " + n + " change sets."); } @Override public void initUndoListFromSelectedRevision() throws DatabaseException { if (null == cs) return; UndoRedoSupport us = session.getService(UndoRedoSupport.class); int n = us.initUndoListFrom(session, cs.getId()); if (DEBUG) System.out.println("DEBUG: Undo list initialised with " + n + " change sets."); } @Override public ChangeSetIdentifier getChangeSetIdentifier() { return cs; } private ChangeSetIdentifier getChangeSetIdentifier(long id) { ManagementSupport ms = session.getService(ManagementSupport.class); Collection cids; try { cids = ms.getChangeSetIdentifiers(id, id); } catch (DatabaseException e) { Logger.defaultLogError(e); return null; } Iterator it = cids.iterator(); while (it.hasNext()) { ChangeSetIdentifier cid = it.next(); if (cid.getId() == id) return cid; } return null; } private void getMetadata() { if(metadata != null) return; else if (null == cs) { metadata = new HashMap(); return; } try { metadata = cs.getMetadata(); if (null == metadata) { ChangeSetIdentifier csid = getChangeSetIdentifier(cs.getId()); if (null != csid) metadata = csid.getMetadata(); } } catch (Exception e) { Logger.defaultLogError(e); } if (null == metadata) metadata = new HashMap(); } // private static final Charset UTF8 = Charset.forName("UTF-8"); // private String toString(byte[] data) { // if (data == null) // return "null"; // CharsetDecoder decoder = UTF8.newDecoder(); // ByteBuffer bbuf = ByteBuffer.wrap(data); // CharBuffer cbuf; // String s = null; // try { // cbuf = decoder.decode(bbuf); // s = cbuf.toString(); // } catch (CharacterCodingException e) { // bbuf.rewind(); // try { // cbuf = UTF8 // .newDecoder() // .onMalformedInput(CodingErrorAction.REPLACE) // .onUnmappableCharacter(CodingErrorAction.REPLACE) // .decode(bbuf); // s = cbuf.toString(); // } catch (CharacterCodingException e1) { // return "String conversion error."; // } // } // return s; // } @Override public String toString() { if (null == cs) return ""; else return "change set " + cs.getId(); } @Override boolean hasChildren() { if (null == metadata) getMetadata(); if (null == cs && metadata.isEmpty()) return false; else return true; } @Override Object[] getChildren() { if (null == metadata) getMetadata(); if (null == cs && metadata.isEmpty()) return new Object[0]; ArrayList objects = new ArrayList(); if (!metadata.isEmpty()) { objects.add(new CommentStringElement("Metaclass", "Count is " + metadata.size() + ".")); CommitMetadata commitMetadata = getMetadata(session, metadata, CommitMetadata.class); if (null != commitMetadata) { if (commitMetadata.opid != 0 && commitMetadata.opid != cs.getId()) objects.add(new StringElement("Part of operation", "" + commitMetadata.opid)); } CommentMetadata commentMetadata = getMetadata(session, metadata, CommentMetadata.class); if (null != commentMetadata) objects.add(new DisplayElement("Comment", commentMetadata.toString())); UndoMetadata undoMetadata = getMetadata(session, metadata, UndoMetadata.class); if (null != undoMetadata) { String header = undoMetadata.getHeader(); objects.add(new DisplayElement(header, undoMetadata.toString())); } } if (cs.getId() > 0) objects.add(new ChangeSetDisplayElement("Change Set", session, cs.getId())); return objects.toArray(); } Image getIdImage() { return Images.getInstance().CHANGE_SET_IMAGE; } String getIdText() { if (null != cs) return "" + cs.getId(); else return ""; } String getDateText() { if (null == metadata) getMetadata(); if (null == cs || metadata.isEmpty()) return ""; CommitMetadata commitMetadata = getMetadata(session, metadata, CommitMetadata.class); if (null != commitMetadata) return commitMetadata.date.toString(); else return ""; } String getCommentText() { if (null == metadata) getMetadata(); if (null == cs || metadata.isEmpty()) return ""; CommentMetadata commentMetadata = getMetadata(session, metadata, CommentMetadata.class); if (null != commentMetadata) { UndoMetadata undoMetadata = getMetadata(session, metadata, UndoMetadata.class); String t = commentMetadata.toString(); if (null == undoMetadata) return t; else return undoMetadata.getHeader() + ": " + t; } else return ""; } } abstract class AbstractColumnLabelProvider extends ColumnLabelProvider { } class IdColumnLabelProvider extends AbstractColumnLabelProvider { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); if (!(element instanceof TreeElement)) cell.setText(""); else { TreeElement te = (TreeElement)element; String text = te.getIdText(); if (null != text) cell.setText(text); Image image = te.getIdImage(); if (null!= image) cell.setImage(image); } } } class DateColumnLabelProvider extends AbstractColumnLabelProvider { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); if (!(element instanceof TreeElement)) cell.setText(""); else { TreeElement te = (TreeElement)element; String text = te.getDateText(); if (null != text) cell.setText(text); Image image = te.getDateImage(); if (null!= image) cell.setImage(image); } } } class CommentColumnLabelProvider extends AbstractColumnLabelProvider { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); if (!(element instanceof TreeElement)) cell.setText(""); else { TreeElement te = (TreeElement)element; String text = te.getCommentText(); if (null != text) cell.setText(text); Image image = te.getCommentImage(); if (null!= image) cell.setImage(image); } } } abstract class ChangeSetProvider implements ITreeContentProvider, ManagementSupport.ChangeSetListener { static final boolean DEBUG = false; protected final Session session; protected final ManagementSupport managementSupport; private boolean subscribed = false; protected Viewer viewer; ChangeSetProvider(Session session) { this.session = session; this.managementSupport = session.getService(ManagementSupport.class); subscribe(); } abstract public Object[] getElements(Object inputElement); @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { this.viewer = viewer; subscribe(); } @Override public void dispose() { managementSupport.cancel(this); this.subscribed = false; } @Override public void onChanged(long csid) { if (null != viewer && this.subscribed) refresh(); } @Override public boolean hasChildren(Object element) { if (element instanceof TreeElement) return ((TreeElement)element).hasChildren(); return false; } @Override public Object getParent(Object element) { return null; } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof TreeElement) return ((TreeElement)parentElement).getChildren(); else return null; } protected void subscribe() { if (this.subscribed) return; managementSupport.subscribe(this); this.subscribed = true; } protected void refresh() { if (viewer == null) return; SWTUtils.asyncExec(viewer.getControl(), new Runnable() { @Override public void run() { if (!viewer.getControl().isDisposed()) viewer.refresh(); } }); } }