/*******************************************************************************
* Copyright (c) 2011, 2012 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.tcf.internal.debug.ui.ColorCache;
import org.eclipse.tcf.protocol.Protocol;
/**
* TCFSnapshot is used to create snapshots of debug views presentation data.
* Such snapshots are used to implement various view update policies.
*/
class TCFSnapshot {
private final IPresentationContext ctx;
private final HashMap<TCFNode,PresentationData> cache = new HashMap<TCFNode,PresentationData>();
private final String[] columns;
private boolean ignore_bg_color = true;
private class PresentationData implements IChildrenCountUpdate, IChildrenUpdate, ILabelUpdate, Runnable {
IViewerUpdate update;
Runnable done;
boolean canceled;
IStatus status;
String[] label;
String[] label_next;
FontData[] font_data;
ImageDescriptor[] image_desc;
RGB[] fg_color;
RGB[] bg_color;
boolean label_done;
TCFNode[] children;
boolean children_done;
private final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>();
boolean isStalled() {
if (label == null) return false;
if (label_next == null) return false;
return !Arrays.equals(label, label_next);
}
public IPresentationContext getPresentationContext() {
return ctx;
}
public Object getElement() {
return update.getElement();
}
public TreePath getElementPath() {
return update.getElementPath();
}
public Object getViewerInput() {
return update.getViewerInput();
}
public void setStatus(IStatus status) {
this.status = status;
}
public IStatus getStatus() {
return status;
}
public void done() {
assert false;
}
public void cancel() {
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
public String[] getColumnIds() {
return columns;
}
public void setLabel(String text, int col) {
if (!label_done) {
if (label == null) {
int cnt = columns == null ? 1 : columns.length;
label = new String[cnt];
}
label[col] = text;
}
else {
if (label_next == null) {
int cnt = columns == null ? 1 : columns.length;
label_next = new String[cnt];
}
label_next[col] = text;
}
}
public void setFontData(FontData fnt, int col) {
if (!label_done) {
if (font_data == null) {
int cnt = columns == null ? 1 : columns.length;
font_data = new FontData[cnt];
}
font_data[col] = fnt;
}
}
public void setImageDescriptor(ImageDescriptor image, int col) {
if (!label_done) {
if (image_desc == null) {
int cnt = columns == null ? 1 : columns.length;
image_desc = new ImageDescriptor[cnt];
}
image_desc[col] = image;
}
}
public void setForeground(RGB rgb, int col) {
if (!label_done) {
if (fg_color == null) {
int cnt = columns == null ? 1 : columns.length;
fg_color = new RGB[cnt];
}
fg_color[col] = rgb;
}
}
public void setBackground(RGB rgb, int col) {
if (!label_done) {
if (bg_color == null) {
int cnt = columns == null ? 1 : columns.length;
bg_color = new RGB[cnt];
}
bg_color[col] = rgb;
}
}
public int getOffset() {
return 0;
}
public int getLength() {
return children.length;
}
public void setChild(Object child, int offset) {
if (!children_done) {
children[offset] = (TCFNode)child;
}
}
public void setChildCount(int cnt) {
if (!children_done) {
children = new TCFNode[cnt];
}
}
public void run() {
Runnable d = done;
update = null;
done = null;
for (Runnable r : waiting_list) Protocol.invokeLater(r);
waiting_list.clear();
d.run();
}
}
private PresentationData data;
TCFSnapshot(IPresentationContext ctx) {
this.ctx = ctx;
columns = ctx.getColumns();
}
void dispose() {
for (PresentationData d : cache.values()) {
for (Runnable r : d.waiting_list) Protocol.invokeLater(r);
}
cache.clear();
}
/**
* Retrieve children count for a presentation context.
* The method is always called on TCF dispatch thread.
* @param update - children count update request.
* @param node - debug model node.
* @param done - client call back interface, during data waiting it is
* called every time new portion of data becomes available.
* @return false if waiting data retrieval, true if all done.
*/
public boolean getData(IChildrenCountUpdate update, TCFNode node, Runnable done) {
if (!getChildren(update, node, done)) return false;
update.setChildCount(data.children.length);
return true;
}
/**
* Retrieve children for a presentation context.
* The method is always called on TCF dispatch thread.
* @param update - children update request.
* @param node - debug model node.
* @param done - client call back interface, during data waiting it is
* called every time new portion of data becomes available.
* @return false if waiting data retrieval, true if all done.
*/
public boolean getData(IChildrenUpdate update, TCFNode node, Runnable done) {
if (!getChildren(update, node, done)) return false;
int offset = 0;
int r_offset = update.getOffset();
int r_length = update.getLength();
for (TCFNode n : data.children) {
if (offset >= r_offset && offset < r_offset + r_length) {
update.setChild(n, offset);
}
offset++;
}
return true;
}
/**
* Check if the node has children in a presentation context.
* The method is always called on TCF dispatch thread.
* @param update - "has children" update request.
* @param node - debug model node.
* @param done - client call back interface, during data waiting it is
* called every time new portion of data becomes available.
* @return false if waiting data retrieval, true if all done.
*/
public boolean getData(IHasChildrenUpdate update, TCFNode node, Runnable done) {
if (!getChildren(update, node, done)) return false;
update.setHasChilren(data.children.length > 0);
return true;
}
/**
* Retrieve node label for a presentation context.
* The method is always called on TCF dispatch thread.
* @param update - label update request.
* @param node - debug model node.
* @param done - client call back interface, during data waiting it is
* called every time new portion of data becomes available.
* @return false if waiting data retrieval, true if all done.
*/
public boolean getData(ILabelUpdate update, TCFNode node, Runnable done) {
if (!getLabel(update, node, done)) return false;
String[] ids_update = update.getColumnIds();
String[] ids_data = columns;
if (ids_update != ids_data && !Arrays.equals(ids_update, ids_data)) {
int n = ids_update == null ? 1 : ids_update.length;
for (int i = 0; i < n; i++) update.setBackground(ColorCache.rgb_stalled, i);
}
else {
if (data.label != null) {
for (int i = 0; i < data.label.length; i++) {
if (data.label[i] != null) update.setLabel(data.label[i], i);
}
}
if (data.font_data != null) {
for (int i = 0; i < data.font_data.length; i++) {
if (data.font_data[i] != null) update.setFontData(data.font_data[i], i);
}
}
if (data.image_desc != null) {
for (int i = 0; i < data.image_desc.length; i++) {
if (data.image_desc[i] != null) update.setImageDescriptor(data.image_desc[i], i);
}
}
if (data.isStalled()) {
int n = ids_update == null ? 1 : ids_update.length;
for (int i = 0; i < n; i++) update.setForeground(ColorCache.rgb_stalled, i);
}
else {
if (data.fg_color != null) {
for (int i = 0; i < data.fg_color.length; i++) {
if (data.fg_color[i] != null) update.setForeground(data.fg_color[i], i);
}
}
}
if (!ignore_bg_color && data.bg_color != null) {
for (int i = 0; i < data.bg_color.length; i++) {
if (data.bg_color[i] != null) update.setBackground(data.bg_color[i], i);
}
}
}
return true;
}
private boolean getChildren(IViewerUpdate update, TCFNode node, Runnable done) {
data = cache.get(node);
if (data == null) cache.put(node, data = new PresentationData());
assert data.update != update;
if (data.children_done) return true;
if (data.update != null) {
data.waiting_list.add(done);
return false;
}
data.update = update;
data.done = done;
if (data.children == null) {
if (!node.getData((IChildrenCountUpdate)data, data)) return false;
assert data.children != null;
}
if (!node.getData((IChildrenUpdate)data, data)) return false;
data.children_done = true;
data.update = null;
data.done = null;
return true;
}
private boolean getLabel(IViewerUpdate update, TCFNode node, Runnable done) {
data = cache.get(node);
if (data == null) cache.put(node, data = new PresentationData());
assert data.update != update;
if (data.update != null) {
data.waiting_list.add(done);
return false;
}
data.update = update;
data.done = done;
if (!node.getData((ILabelUpdate)data, data)) return false;
data.label_done = true;
data.update = null;
data.done = null;
return true;
}
}