/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.pivot.tutorials.explorer.tools;
import java.lang.reflect.Method;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import org.apache.pivot.beans.BXMLSerializer;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.collections.Sequence.Tree.Path;
import org.apache.pivot.serialization.SerializationException;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.TableView;
import org.apache.pivot.wtk.TreeView;
import org.apache.pivot.wtk.TreeViewNodeStateListener;
import org.apache.pivot.wtk.content.TreeBranch;
import org.apache.pivot.wtk.content.TreeNode;
import org.apache.pivot.wtk.skin.ContainerSkin;
class EventLoggerSkin extends ContainerSkin implements EventLogger.Skin, EventLoggerListener {
private static class TreeNodeComparator implements Comparator<TreeNode> {
public int compare(TreeNode treeNode1, TreeNode treeNode2) {
return treeNode1.getText().compareTo(treeNode2.getText());
}
}
private static class EventNode extends TreeNode {
private Method event;
public EventNode(Method event) {
super(event.getName());
this.event = event;
}
public Method getEvent() {
return event;
}
}
private Component content = null;
private TreeView declaredEventsTreeView = null;
private TableView firedEventsTableView = null;
private boolean updating = false;
private static TreeNodeComparator treeNodeComparator = new TreeNodeComparator();
@Override
public void install(Component component) {
super.install(component);
EventLogger eventLogger = (EventLogger)component;
eventLogger.getEventLoggerListeners().add(this);
BXMLSerializer bxmlSerializer = new BXMLSerializer();
try {
content = (Component)bxmlSerializer.readObject(EventLoggerSkin.class,
"event_logger_skin.bxml", true);
} catch (IOException exception) {
throw new RuntimeException(exception);
} catch (SerializationException exception) {
throw new RuntimeException(exception);
}
eventLogger.add(content);
declaredEventsTreeView = (TreeView)bxmlSerializer.getNamespace().get("declaredEventsTreeView");
firedEventsTableView = (TableView)bxmlSerializer.getNamespace().get("firedEventsTableView");
// Propagate check state upwards or downwards as necessary
declaredEventsTreeView.getTreeViewNodeStateListeners().add(new TreeViewNodeStateListener() {
@Override
public void nodeCheckStateChanged(TreeView treeView, Path path,
TreeView.NodeCheckState previousCheckState) {
TreeView.NodeCheckState checkState = treeView.getNodeCheckState(path);
if (!updating) {
// Set the updating flag for the life of this event loop
updating = true;
ApplicationContext.queueCallback(new Runnable() {
@Override
public void run() {
updating = false;
}
});
EventLogger eventLogger = (EventLogger)getComponent();
boolean checked = (checkState == TreeView.NodeCheckState.CHECKED);
List<?> treeData = treeView.getTreeData();
TreeNode treeNode = (TreeNode)Sequence.Tree.get(treeData, path);
if (treeNode instanceof List<?>) {
if (previousCheckState == TreeView.NodeCheckState.CHECKED
|| checkState == TreeView.NodeCheckState.CHECKED) {
// Propagate downward
List<?> treeBranch = (List<?>)treeNode;
Path childPath = new Path(path);
int lastIndex = childPath.getLength();
childPath.add(0);
for (int i = 0, n = treeBranch.getLength(); i < n; i++) {
childPath.update(lastIndex, i);
treeView.setNodeChecked(childPath, checked);
EventNode eventNode = (EventNode)treeBranch.get(i);
Method event = eventNode.getEvent();
if (checked) {
eventLogger.getIncludeEvents().add(event);
} else {
eventLogger.getIncludeEvents().remove(event);
}
}
}
} else {
Path parentPath = new Path(path, path.getLength() - 1);
EventNode eventNode = (EventNode)treeNode;
Method event = eventNode.getEvent();
if (checked) {
List<?> treeBranch = (List<?>)Sequence.Tree.get(treeData, parentPath);
Path childPath = new Path(path);
int lastIndex = parentPath.getLength();
int i = 0, n = treeBranch.getLength();
while (i < n) {
childPath.update(lastIndex, i);
if (!treeView.isNodeChecked(childPath)) {
break;
}
i++;
}
if (i == n) {
// Propagate upward
treeView.setNodeChecked(parentPath, checked);
}
eventLogger.getIncludeEvents().add(event);
} else {
// Propagate upward
treeView.setNodeChecked(parentPath, checked);
eventLogger.getIncludeEvents().remove(event);
}
}
}
}
});
sourceChanged(eventLogger, null);
}
@Override
public int getPreferredWidth(int height) {
return content.getPreferredWidth(height);
}
@Override
public int getPreferredHeight(int width) {
return content.getPreferredHeight(width);
}
@Override
public Dimensions getPreferredSize() {
return content.getPreferredSize();
}
@Override
public void layout() {
content.setLocation(0, 0);
content.setSize(getWidth(), getHeight());
}
// EventLogger.Skin methods
@Override
public void clearLog() {
firedEventsTableView.getTableData().clear();
}
// EventLoggerListener methods
@Override
public void sourceChanged(EventLogger eventLogger, Component previousSource) {
//Component source = eventLogger.getSource();
HashMap<Class<?>, ArrayList<Method>> buckets = new HashMap<Class<?>, ArrayList<Method>>();
for (Method event : eventLogger.getDeclaredEvents()) {
Class<?> listenerInterface = event.getDeclaringClass();
ArrayList<Method> bucket = buckets.get(listenerInterface);
if (bucket == null) {
bucket = new ArrayList<Method>();
buckets.put(listenerInterface, bucket);
}
bucket.add(event);
}
ArrayList<TreeNode> treeData = new ArrayList<TreeNode>(treeNodeComparator);
declaredEventsTreeView.setTreeData(treeData);
updating = true;
try {
for (Class<?> listenerInterface : buckets) {
TreeBranch treeBranch = new TreeBranch(listenerInterface.getSimpleName());
treeBranch.setComparator(treeNodeComparator);
treeData.add(treeBranch);
for (Method event : buckets.get(listenerInterface)) {
treeBranch.add(new EventNode(event));
eventLogger.getIncludeEvents().add(event);
}
}
Sequence.Tree.ItemIterator<TreeNode> iter = Sequence.Tree.depthFirstIterator(treeData);
while (iter.hasNext()) {
iter.next();
declaredEventsTreeView.setNodeChecked(iter.getPath(), true);
}
} finally {
updating = false;
}
}
@Override
public void eventIncluded(EventLogger eventLogger, Method method) {
setEventIncluded(method, true);
}
@Override
public void eventExcluded(EventLogger eventLogger, Method method) {
setEventIncluded(method, false);
}
@SuppressWarnings("unchecked")
private void setEventIncluded(Method event, boolean included) {
List<TreeNode> treeData = (List<TreeNode>)declaredEventsTreeView.getTreeData();
Sequence.Tree.ItemIterator<TreeNode> iter = Sequence.Tree.depthFirstIterator(treeData);
while (iter.hasNext()) {
TreeNode treeNode = iter.next();
if (treeNode instanceof EventNode) {
EventNode eventNode = (EventNode)treeNode;
if (eventNode.getEvent() == event) {
declaredEventsTreeView.setNodeChecked(iter.getPath(), included);
break;
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public void eventFired(EventLogger eventLogger, Method event, Object[] arguments) {
HashMap<String, Object> row = new HashMap<String, Object>();
row.put("interface", event.getDeclaringClass().getSimpleName());
row.put("method", event.getName());
row.put("arguments", Arrays.toString(arguments));
List<Object> tableData = (List<Object>)firedEventsTableView.getTableData();
final int rowIndex = tableData.add(row);
ApplicationContext.queueCallback(new Runnable() {
@Override
public void run() {
Bounds rowBounds = firedEventsTableView.getRowBounds(rowIndex);
firedEventsTableView.scrollAreaToVisible(rowBounds);
}
});
}
}