blob: b8444427ce029ed2b04165d4e6c38e54b8537542 [file] [log] [blame]
/*
* 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.netbeans.modules.viewmodel;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.netbeans.spi.viewmodel.CheckNodeModel;
import org.netbeans.spi.viewmodel.ColumnModel;
import org.netbeans.spi.viewmodel.ExtendedNodeModel;
import org.netbeans.spi.viewmodel.ModelEvent;
import org.netbeans.spi.viewmodel.ModelListener;
import org.netbeans.spi.viewmodel.TableModel;
import org.netbeans.spi.viewmodel.TreeModel;
import org.netbeans.spi.viewmodel.UnknownTypeException;
import org.openide.util.datatransfer.PasteType;
/**
* Counted model
*
* @author Martin Entlicher
*/
class CountedModel implements TreeModel, ExtendedNodeModel, CheckNodeModel, TableModel {
private static final String COLUMN_UC = "uppercase";
private static final String COLUMN_LC = "lowercase";
private static final String COLUMN_BYTES = "bytes";
private final String[] rootChildren;
private final int depth;
private final Map<Object, Object[]> cachedChildren = new HashMap<Object, Object[]>();
private final List<ModelListener> listeners = new ArrayList<ModelListener>();
private final Map<Object, Boolean> selectedNodes = new HashMap<Object, Boolean>();
private final Map<Object, Map<String, Object>> changedValues = new HashMap<Object, Map<String, Object>>();
private final Set<CountedCall> countedCalls = new HashSet<CountedCall>();
public CountedModel(String[] children, int depth) {
rootChildren = children;
this.depth = depth;
}
public ColumnModel[] createColumns() {
return new ColumnModel[] {
new CountedColumn(COLUMN_LC),
new CountedColumn(COLUMN_UC),
new CountedColumn(COLUMN_BYTES),
};
}
public Object getRoot() {
return ROOT;
}
public Object[] getChildren(Object parent, int from, int to) throws UnknownTypeException {
//System.err.println("\n\nget CHILDREN("+this+", on "+parent);
//System.err.println(" counted calls before = "+getCountedCalls("getChildren", parent)+"\n\n");
countCall("getChildren", parent);
//System.err.println(" counted calls after = "+getCountedCalls("getChildren", parent)+"\n\n");
if (parent == ROOT) {
return rootChildren;
} else {
Object[] ch = cachedChildren.get(parent);
if (ch == null) {
ch = new String[rootChildren.length];
for (int i = 0; i < ch.length; i++) {
ch[i] = parent + "/" + rootChildren[i];
}
cachedChildren.put(parent, ch);
}
return ch;
}
}
public boolean isLeaf(Object node) throws UnknownTypeException {
countCall("isLeaf", node);
String s = (String) node;
int d = 0;
for (int i = 0; (i = s.indexOf('/', i+1)) > 0; d++) ;
return d >= depth;
}
public int getChildrenCount(Object node) throws UnknownTypeException {
countCall("getChildrenCount", node);
return rootChildren.length;
}
public void addModelListener(ModelListener l) {
//System.err.println(this+".addModelListener("+l+")");
//Thread.dumpStack();
synchronized (listeners) {
listeners.add(l);
}
//System.err.println(" listeners = "+listeners);
}
public void removeModelListener(ModelListener l) {
//System.err.println(this+".removeModelListener("+l+")");
//Thread.dumpStack();
synchronized (listeners) {
listeners.remove(l);
}
//System.err.println(" listeners = "+listeners);
}
public boolean canRename(Object node) throws UnknownTypeException {
countCall("canRename", node);
return true;
}
public boolean canCopy(Object node) throws UnknownTypeException {
countCall("canCopy", node);
return true;
}
public boolean canCut(Object node) throws UnknownTypeException {
countCall("canCut", node);
return true;
}
public Transferable clipboardCopy(Object node) throws IOException, UnknownTypeException {
countCall("clipboardCopy", node);
return null;
}
public Transferable clipboardCut(Object node) throws IOException, UnknownTypeException {
countCall("clipboardCut", node);
return null;
}
public PasteType[] getPasteTypes(Object node, Transferable t) throws UnknownTypeException {
countCall("getPasteTypes", node, t);
return new PasteType[] {};
}
public void setName(Object node, String name) throws UnknownTypeException {
countCall("setName", node, name);
}
public String getIconBaseWithExtension(Object node) throws UnknownTypeException {
countCall("getIconBaseWithExtension", node);
return null;
}
public String getDisplayName(Object node) throws UnknownTypeException {
countCall("getDisplayName", node);
return node.toString().replace('/', '-');
}
public String getIconBase(Object node) throws UnknownTypeException {
countCall("getIconBase", node);
return null;
}
public String getShortDescription(Object node) throws UnknownTypeException {
countCall("getShortDescription", node);
return node.toString() + " => " + node.toString().replace('/', '-');
}
public boolean isCheckable(Object node) throws UnknownTypeException {
countCall("isCheckable", node);
return true;
}
public boolean isCheckEnabled(Object node) throws UnknownTypeException {
countCall("isCheckEnabled", node);
return true;
}
public Boolean isSelected(Object node) throws UnknownTypeException {
countCall("isSelected", node);
return selectedNodes.containsKey(node) && selectedNodes.get(node);
}
public void setSelected(Object node, Boolean selected) throws UnknownTypeException {
countCall("setSelected", node);
selectedNodes.put(node, selected);
}
public Object getValueAt(Object node, String columnID) throws UnknownTypeException {
countCall("getValueAt", node, columnID);
Map<String, Object> values = changedValues.get(node);
if (values != null) {
Object value = values.get(columnID);
if (value != null) {
return value;
}
}
if (columnID.equals(COLUMN_LC)) {
return node.toString().toLowerCase();
}
if (columnID.equals(COLUMN_UC)) {
return node.toString().toUpperCase();
}
if (columnID.equals(COLUMN_BYTES)) {
return Arrays.toString(node.toString().getBytes());
}
throw new UnknownTypeException(node+".getValue("+columnID+")");
}
public boolean isReadOnly(Object node, String columnID) throws UnknownTypeException {
countCall("isReadOnly", node, columnID);
return false;
}
public void setValueAt(Object node, String columnID, Object value) throws UnknownTypeException {
countCall("setValueAt", node, columnID, value);
Map<String, Object> values = changedValues.get(node);
if (values == null) {
values = new HashMap<String, Object>();
changedValues.put(node, values);
}
values.put(columnID, value);
}
public void fireModelChangeEvent(ModelEvent event) {
for (ModelListener l : listeners) {
l.modelChanged(event);
}
}
protected void countCall(String methodName, Object... params) {
CountedCall cc = new CountedCall(methodName, params);
if (!countedCalls.add(cc)) {
for (CountedCall ecc : countedCalls) {
if (ecc.equals(cc)) {
ecc.addCall();
}
}
}
}
public CountedCall getMaxCountedCall() {
CountedCall mcc = null;
for (CountedCall ecc : countedCalls) {
if (mcc == null) {
mcc = ecc;
} else if (mcc.numCalls() < ecc.numCalls()) {
mcc = ecc;
}
}
return mcc;
}
public CountedCall[] getCountedCalls(String methodName) {
List<CountedCall> ccs = new ArrayList<CountedCall>();
for (CountedCall cc : countedCalls) {
if (methodName.equals(cc.methodName)) {
ccs.add(cc);
}
}
return ccs.toArray(new CountedCall[] {});
}
public CountedCall getCountedCalls(String methodName, Object... params) {
for (CountedCall cc : countedCalls) {
if (methodName.equals(cc.methodName) && Arrays.equals(params, cc.params)) {
return cc;
}
}
return null;
}
@Override
public String toString() {
return this.getClass().getSimpleName()+"("+Arrays.toString(rootChildren)+")";
}
static class CountedCall {
private final String methodName;
private final Object[] params;
private final List<Throwable> calls = new ArrayList<Throwable>();
CountedCall(String methodName, Object... params) {
this.methodName = methodName;
this.params = params;
addCall();
}
void addCall() {
calls.add(new Counter().fillInStackTrace());
}
int numCalls() {
return calls.size();
}
Throwable[] callStacks() {
return calls.toArray(new Throwable[] {});
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CountedCall) {
CountedCall cc = (CountedCall) obj;
if (!cc.methodName.equals(methodName)) {
return false;
}
if (!Arrays.equals(cc.params, params)) {
return false;
}
return true;
}
return false;
}
@Override
public int hashCode() {
return methodName.hashCode() + (Arrays.hashCode(params) >>> 16);
}
@Override
public String toString() {
String str = "CountedCall("+methodName+", "+Arrays.toString(params)+") called "+numCalls()+" times.\n";
for (int i = 0; i < calls.size(); i++) {
Throwable t = calls.get(i);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.flush();
str = str + " " + (i+1) + ".: " + sw.toString() + "\n";
}
return str;
}
private static class Counter extends Exception {}
}
static class CountedColumn extends ColumnModel {
private final String id;
public CountedColumn(String id) {
this.id = id;
}
@Override
public String getID() {
return id;
}
@Override
public String getDisplayName() {
return "Column "+id;
}
@Override
public Class getType() {
return String.class;
}
}
}