/*
* Copyright (C) 2008 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.hierarchyviewerlib.device;
import org.eclipse.swt.graphics.Image;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class ViewNode {
public static enum ProfileRating {
RED, YELLOW, GREEN, NONE
};
private static final double RED_THRESHOLD = 0.8;
private static final double YELLOW_THRESHOLD = 0.5;
public static final String MISCELLANIOUS = "miscellaneous";
public String id;
public String name;
public String hashCode;
public List<Property> properties = new ArrayList<Property>();
public Map<String, Property> namedProperties = new HashMap<String, Property>();
public ViewNode parent;
public List<ViewNode> children = new ArrayList<ViewNode>();
public int left;
public int top;
public int width;
public int height;
public int scrollX;
public int scrollY;
public int paddingLeft;
public int paddingRight;
public int paddingTop;
public int paddingBottom;
public int marginLeft;
public int marginRight;
public int marginTop;
public int marginBottom;
public int baseline;
public boolean willNotDraw;
public boolean hasMargins;
public boolean hasFocus;
public int index;
public double measureTime;
public double layoutTime;
public double drawTime;
public ProfileRating measureRating = ProfileRating.NONE;
public ProfileRating layoutRating = ProfileRating.NONE;
public ProfileRating drawRating = ProfileRating.NONE;
public Set<String> categories = new TreeSet<String>();
public Window window;
public Image image;
public int imageReferences = 1;
public int viewCount;
public boolean filtered;
public int protocolVersion;
public ViewNode(Window window, ViewNode parent, String data) {
this.window = window;
this.parent = parent;
index = this.parent == null ? 0 : this.parent.children.size();
if (this.parent != null) {
this.parent.children.add(this);
}
int delimIndex = data.indexOf('@');
name = data.substring(0, delimIndex);
data = data.substring(delimIndex + 1);
delimIndex = data.indexOf(' ');
hashCode = data.substring(0, delimIndex);
loadProperties(data.substring(delimIndex + 1).trim());
measureTime = -1;
layoutTime = -1;
drawTime = -1;
}
public void dispose() {
final int N = children.size();
for (int i = 0; i < N; i++) {
children.get(i).dispose();
}
dereferenceImage();
}
public void referenceImage() {
imageReferences++;
}
public void dereferenceImage() {
imageReferences--;
if (image != null && imageReferences == 0) {
image.dispose();
}
}
private void loadProperties(String data) {
int start = 0;
boolean stop;
do {
int index = data.indexOf('=', start);
ViewNode.Property property = new ViewNode.Property();
property.name = data.substring(start, index);
int index2 = data.indexOf(',', index + 1);
int length = Integer.parseInt(data.substring(index + 1, index2));
start = index2 + 1 + length;
property.value = data.substring(index2 + 1, index2 + 1 + length);
properties.add(property);
namedProperties.put(property.name, property);
stop = start >= data.length();
if (!stop) {
start += 1;
}
} while (!stop);
Collections.sort(properties, new Comparator<ViewNode.Property>() {
public int compare(ViewNode.Property source, ViewNode.Property destination) {
return source.name.compareTo(destination.name);
}
});
id = namedProperties.get("mID").value; //$NON-NLS-1$
left =
namedProperties.containsKey("mLeft") ? getInt("mLeft", 0) : getInt("layout:mLeft", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0);
top = namedProperties.containsKey("mTop") ? getInt("mTop", 0) : getInt("layout:mTop", 0); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
width =
namedProperties.containsKey("getWidth()") ? getInt("getWidth()", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"layout:getWidth()", 0); //$NON-NLS-1$
height =
namedProperties.containsKey("getHeight()") ? getInt("getHeight()", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"layout:getHeight()", 0); //$NON-NLS-1$
scrollX =
namedProperties.containsKey("mScrollX") ? getInt("mScrollX", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"scrolling:mScrollX", 0); //$NON-NLS-1$
scrollY =
namedProperties.containsKey("mScrollY") ? getInt("mScrollY", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"scrolling:mScrollY", 0); //$NON-NLS-1$
paddingLeft =
namedProperties.containsKey("mPaddingLeft") ? getInt("mPaddingLeft", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"padding:mPaddingLeft", 0); //$NON-NLS-1$
paddingRight =
namedProperties.containsKey("mPaddingRight") ? getInt("mPaddingRight", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"padding:mPaddingRight", 0); //$NON-NLS-1$
paddingTop =
namedProperties.containsKey("mPaddingTop") ? getInt("mPaddingTop", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"padding:mPaddingTop", 0); //$NON-NLS-1$
paddingBottom =
namedProperties.containsKey("mPaddingBottom") ? getInt("mPaddingBottom", 0) //$NON-NLS-1$ //$NON-NLS-2$
: getInt("padding:mPaddingBottom", 0); //$NON-NLS-1$
marginLeft =
namedProperties.containsKey("layout_leftMargin") ? getInt("layout_leftMargin", //$NON-NLS-1$ //$NON-NLS-2$
Integer.MIN_VALUE) : getInt("layout:layout_leftMargin", Integer.MIN_VALUE); //$NON-NLS-1$
marginRight =
namedProperties.containsKey("layout_rightMargin") ? getInt("layout_rightMargin", //$NON-NLS-1$ //$NON-NLS-2$
Integer.MIN_VALUE) : getInt("layout:layout_rightMargin", Integer.MIN_VALUE); //$NON-NLS-1$
marginTop =
namedProperties.containsKey("layout_topMargin") ? getInt("layout_topMargin", //$NON-NLS-1$ //$NON-NLS-2$
Integer.MIN_VALUE) : getInt("layout:layout_topMargin", Integer.MIN_VALUE); //$NON-NLS-1$
marginBottom =
namedProperties.containsKey("layout_bottomMargin") ? getInt("layout_bottomMargin", //$NON-NLS-1$ //$NON-NLS-2$
Integer.MIN_VALUE)
: getInt("layout:layout_bottomMargin", Integer.MIN_VALUE); //$NON-NLS-1$
baseline =
namedProperties.containsKey("getBaseline()") ? getInt("getBaseline()", 0) : getInt( //$NON-NLS-1$ //$NON-NLS-2$
"layout:getBaseline()", 0); //$NON-NLS-1$
willNotDraw =
namedProperties.containsKey("willNotDraw()") ? getBoolean("willNotDraw()", false) //$NON-NLS-1$ //$NON-NLS-2$
: getBoolean("drawing:willNotDraw()", false); //$NON-NLS-1$
hasFocus =
namedProperties.containsKey("hasFocus()") ? getBoolean("hasFocus()", false) //$NON-NLS-1$ //$NON-NLS-2$
: getBoolean("focus:hasFocus()", false); //$NON-NLS-1$
hasMargins =
marginLeft != Integer.MIN_VALUE && marginRight != Integer.MIN_VALUE
&& marginTop != Integer.MIN_VALUE && marginBottom != Integer.MIN_VALUE;
for (String name : namedProperties.keySet()) {
int index = name.indexOf(':');
if (index != -1) {
categories.add(name.substring(0, index));
}
}
if (categories.size() != 0) {
categories.add(MISCELLANIOUS);
}
}
public void setProfileRatings() {
final int N = children.size();
if (N > 1) {
double totalMeasure = 0;
double totalLayout = 0;
double totalDraw = 0;
for (int i = 0; i < N; i++) {
ViewNode child = children.get(i);
totalMeasure += child.measureTime;
totalLayout += child.layoutTime;
totalDraw += child.drawTime;
}
for (int i = 0; i < N; i++) {
ViewNode child = children.get(i);
if (child.measureTime / totalMeasure >= RED_THRESHOLD) {
child.measureRating = ProfileRating.RED;
} else if (child.measureTime / totalMeasure >= YELLOW_THRESHOLD) {
child.measureRating = ProfileRating.YELLOW;
} else {
child.measureRating = ProfileRating.GREEN;
}
if (child.layoutTime / totalLayout >= RED_THRESHOLD) {
child.layoutRating = ProfileRating.RED;
} else if (child.layoutTime / totalLayout >= YELLOW_THRESHOLD) {
child.layoutRating = ProfileRating.YELLOW;
} else {
child.layoutRating = ProfileRating.GREEN;
}
if (child.drawTime / totalDraw >= RED_THRESHOLD) {
child.drawRating = ProfileRating.RED;
} else if (child.drawTime / totalDraw >= YELLOW_THRESHOLD) {
child.drawRating = ProfileRating.YELLOW;
} else {
child.drawRating = ProfileRating.GREEN;
}
}
}
for (int i = 0; i < N; i++) {
children.get(i).setProfileRatings();
}
}
public void setViewCount() {
viewCount = 1;
final int N = children.size();
for (int i = 0; i < N; i++) {
ViewNode child = children.get(i);
child.setViewCount();
viewCount += child.viewCount;
}
}
public void filter(String text) {
int dotIndex = name.lastIndexOf('.');
String shortName = (dotIndex == -1) ? name : name.substring(dotIndex + 1);
filtered =
!text.equals("") //$NON-NLS-1$
&& (shortName.toLowerCase().contains(text.toLowerCase()) || (!id
.equals("NO_ID") && id.toLowerCase().contains(text.toLowerCase()))); //$NON-NLS-1$
final int N = children.size();
for (int i = 0; i < N; i++) {
children.get(i).filter(text);
}
}
private boolean getBoolean(String name, boolean defaultValue) {
Property p = namedProperties.get(name);
if (p != null) {
try {
return Boolean.parseBoolean(p.value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
return defaultValue;
}
private int getInt(String name, int defaultValue) {
Property p = namedProperties.get(name);
if (p != null) {
try {
return Integer.parseInt(p.value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
return defaultValue;
}
@Override
public String toString() {
return name + "@" + hashCode; //$NON-NLS-1$
}
public static class Property {
public String name;
public String value;
@Override
public String toString() {
return name + '=' + value;
}
}
}