blob: fd9fdabca18481469d47d40fb0baefab4fb0b6a8 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.editors.gfxtrace.controllers;
import com.android.tools.idea.ddms.EdtExecutor;
import com.android.tools.idea.editors.gfxtrace.GfxTraceEditor;
import com.android.tools.idea.editors.gfxtrace.controllers.modeldata.StateTreeNode;
import com.android.tools.idea.editors.gfxtrace.renderers.StateTreeRenderer;
import com.android.tools.idea.editors.gfxtrace.renderers.styles.TreeUtil;
import com.android.tools.idea.editors.gfxtrace.rpc.*;
import com.android.tools.idea.editors.gfxtrace.schema.*;
import com.android.tools.rpclib.binary.Decoder;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.treeStructure.SimpleTree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
public class StateController implements GfxController {
@NotNull private static final Logger LOG = Logger.getInstance(StateController.class);
@NotNull private final GfxTraceEditor myEditor;
@NotNull private final JBLoadingPanel myLoadingPanel;
@NotNull private final SimpleTree myTree;
@NotNull private AtomicLong myAtomicAtomId = new AtomicLong(-1);
@Nullable private StructInfo myStateHierarchy;
public StateController(@NotNull GfxTraceEditor editor, @NotNull JBScrollPane scrollPane) {
myEditor = editor;
myTree = new SimpleTree();
myTree.setRowHeight(TreeUtil.TREE_ROW_HEIGHT);
myTree.setRootVisible(false);
myTree.setCellRenderer(new StateTreeRenderer());
myTree.getEmptyText().setText(SELECT_CAPTURE);
myLoadingPanel = new JBLoadingPanel(new BorderLayout(), editor.getProject());
myLoadingPanel.add(myTree);
scrollPane.setViewportView(myLoadingPanel);
}
@Nullable
private static DefaultMutableTreeNode constructStateNode(@NotNull String name, @NotNull TypeKind typeKind, @Nullable Object value) {
StateTreeNode thisNode = null;
switch (typeKind) {
case Bool:
case S8:
case U8:
case S16:
case U16:
case S32:
case U32:
case S64:
case U64:
case F32:
case F64:
case String:
case Pointer:
thisNode = new StateTreeNode(name, value);
break;
case Enum:
assert (value instanceof EnumValue);
EnumValue enumValue = (EnumValue)value;
for (EnumEntry entry : enumValue.info.getEntries()) {
if (enumValue.value == entry.getValue()) {
thisNode = new StateTreeNode(name, entry.getName());
break;
}
}
if (thisNode == null) {
LOG.warn("Invalid bitfield value passed in: " + enumValue.value);
}
break;
case Array:
assert (value instanceof Array);
Array array = (Array)value;
thisNode = new StateTreeNode(name, null);
ArrayInfo arrayInfo = array.info;
Object[] elements = array.elements;
int i = 0;
for (Object element : elements) {
DefaultMutableTreeNode childNode = constructStateNode(Integer.toString(i++), arrayInfo.getElementType().getKind(), element);
if (childNode != null) {
thisNode.add(childNode);
}
}
break;
case Struct:
assert (value instanceof Struct);
Struct struct = (Struct)value;
thisNode = new StateTreeNode(name, null);
for (Field field : struct.fields) {
DefaultMutableTreeNode childNode = constructStateNode(field.info.getName(), field.info.getType().getKind(), field.value);
if (childNode != null) {
thisNode.add(childNode);
}
}
break;
case Class:
assert (value instanceof com.android.tools.idea.editors.gfxtrace.schema.Class);
com.android.tools.idea.editors.gfxtrace.schema.Class gfxClass = (com.android.tools.idea.editors.gfxtrace.schema.Class)value;
thisNode = new StateTreeNode(name, null);
for (Field field : gfxClass.fields) {
DefaultMutableTreeNode childNode = constructStateNode(field.info.getName(), field.info.getType().getKind(), field.value);
if (childNode != null) {
thisNode.add(childNode);
}
}
break;
case Map:
assert (value instanceof Map);
Map map = (Map)value;
thisNode = new StateTreeNode(name, null);
for (MapEntry mapEntry : map.elements) {
DefaultMutableTreeNode childNode = constructStateNode(mapEntry.key.toString(), map.info.getValueType().getKind(), mapEntry.value);
if (childNode != null) {
thisNode.add(childNode);
}
}
break;
case Memory:
// Skip, since this is not supported at the moment.
thisNode = null;
break;
case Any:
// Skip, since this is not supported at the moment.
thisNode = null;
break;
default:
throw new RuntimeException("Attempting to decode unsupported types.");
}
return thisNode;
}
@Override
public void startLoad() {
myTree.getEmptyText().setText("");
}
@Override
public void commitData(@NotNull GfxContextChangeState state) {
myStateHierarchy = state.myCaptureChangeState.mySchema.getState();
myTree.getEmptyText().setText("Select an atom");
}
public void updateTreeModelFromAtomId(final long atomId) {
ApplicationManager.getApplication().assertIsDispatchThread();
assert (myEditor.getCaptureId() != null);
assert (myEditor.getContext() != null);
assert (myStateHierarchy != null);
final Client client = myEditor.getClient();
final StructInfo stateHierarchy = myStateHierarchy;
final CaptureId captureId = myEditor.getCaptureId();
final int contextId = myEditor.getContext();
myAtomicAtomId.set(atomId);
if (!myLoadingPanel.isLoading()) {
myTree.getEmptyText().setText("");
myLoadingPanel.startLoading();
}
ListenableFuture<TreeNode> nodeFuture = myEditor.getService().submit(new Callable<TreeNode>() {
@Override
@Nullable
public TreeNode call() throws Exception {
BinaryId binaryId = client.GetState(captureId, contextId, atomId).get();
Binary stateBinary = client.ResolveBinary(binaryId).get();
Struct stateStruct = (Struct)Unpack.Type(stateHierarchy, new Decoder(new ByteArrayInputStream(stateBinary.getData())));
return constructStateNode(stateStruct.info.getName(), TypeKind.Struct, stateStruct);
}
});
Futures.addCallback(nodeFuture, new FutureCallback<TreeNode>() {
@Override
public void onSuccess(@Nullable TreeNode result) {
if (myAtomicAtomId.get() != atomId) {
return;
}
myTree.setModel(new DefaultTreeModel(result));
myTree.updateUI();
myLoadingPanel.stopLoading();
}
@Override
public void onFailure(@NotNull Throwable t) {
myLoadingPanel.stopLoading();
LOG.error(t);
}
}, EdtExecutor.INSTANCE);
}
@Override
public void clear() {
myStateHierarchy = null;
clearCache();
}
@Override
public void clearCache() {
myAtomicAtomId.set(-1);
myTree.setModel(null);
myTree.updateUI();
}
}