org.netbeans.modules.viewmodel.TreeModelNode Maven / Gradle / Ivy
/*
* 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.Image;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.lang.ref.WeakReference;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.PrivilegedAction;
import java.text.Format;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.netbeans.spi.viewmodel.AsynchronousModelFilter;
import org.netbeans.spi.viewmodel.AsynchronousModelFilter.CALL;
import org.netbeans.spi.viewmodel.ColumnModel;
import org.netbeans.spi.viewmodel.ModelEvent;
import org.netbeans.spi.viewmodel.Models;
import org.netbeans.spi.viewmodel.Models.TreeFeatures;
import org.netbeans.spi.viewmodel.UnknownTypeException;
import org.netbeans.swing.etable.ETableColumn;
import org.openide.awt.Actions;
import org.openide.explorer.view.CheckableNode;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Index;
import org.openide.nodes.Node;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.RequestProcessor.Task;
import org.openide.util.datatransfer.PasteType;
import org.openide.util.lookup.Lookups;
/**
*
* @author Jan Jancura
*/
public class TreeModelNode extends AbstractNode {
/**
* The maximum length of text that is interpreted as HTML.
* This is documented at openide/explorer/src/org/openide/explorer/doc-files/propertyViewCustomization.html
*/
private static final int MAX_HTML_LENGTH = 511;
private static final String HTML_START_TAG = "";
private static final String HTML_END_TAG = "";
// variables ...............................................................
private Models.CompoundModel model;
private final ColumnModel[] columns;
protected TreeModelRoot treeModelRoot;
protected Object object;
private final LazyChildrenFactory lazyChildren;
private String displayName, oldDisplayName;
private String htmlDisplayName;
private final Object displayNameLock = new Object();
private boolean iconLoaded;
private String shortDescription;
private final Object shortDescriptionLock = new Object();
private final Map properties = new HashMap();
private final Map columnIDsMap;
private static final String EVALUATING_STR = NbBundle.getMessage(TreeModelNode.class, "EvaluatingProp");
// init ....................................................................
/**
* Creates root of call stack for given producer.
*/
public TreeModelNode (
final Models.CompoundModel model,
final TreeModelRoot treeModelRoot,
final Object object
) {
this(
model,
model.getColumns (),
treeModelRoot,
object
);
}
/**
* Creates root of call stack for given producer.
*/
public TreeModelNode (
final Models.CompoundModel model,
final ColumnModel[] columns,
final TreeModelRoot treeModelRoot,
final Object object
) {
this(
model,
columns,
object != model.getRoot() ?
new LazyChildrenFactory(model, columns, treeModelRoot, object) : null,
object != model.getRoot() ?
null : createChildren (model, columns, treeModelRoot, object),
treeModelRoot,
object
);
}
/**
* Creates root of call stack for given producer.
*/
protected TreeModelNode (
final Models.CompoundModel model,
final ColumnModel[] columns,
final LazyChildrenFactory lazyChildren,
final Children children,
final TreeModelRoot treeModelRoot,
final Object object
) {
this(
model,
columns,
lazyChildren,
children,
treeModelRoot,
object,
new Index[] { null });
}
private TreeModelNode (
final Models.CompoundModel model,
final ColumnModel[] columns,
final LazyChildrenFactory lazyChildren,
final Children children,
final TreeModelRoot treeModelRoot,
final Object object,
final Index[] indexPtr // Hack, because we can not declare variables before call to super() :-(
) {
super (
(lazyChildren != null) ?
Children.createLazy(lazyChildren) : children,
createLookup(object, model, children, indexPtr)
);
this.model = model;
this.treeModelRoot = treeModelRoot;
this.object = object;
this.lazyChildren = lazyChildren;
if (indexPtr[0] != null) {
((IndexImpl) indexPtr[0]).setNode(this);
setIndexWatcher(indexPtr[0]);
}
//
// Use the modified CompoundModel class's field to set the
// propertiesHelpID for properties sheets if the model's helpID
// has been set
if (model.getHelpId() != null) {
this.setValue("propertiesHelpID", model.getHelpId()); // NOI18N
}
//
treeModelRoot.registerNode (object, this);
this.columnIDsMap = createColumnIDsMap(columns);
this.columns = columns;
}
private static Lookup createLookup(Object object, Models.CompoundModel model,
Children ch, Index[] indexPtr) {
CheckNodeCookieImpl cnc = new CheckNodeCookieImpl(model, object);
boolean canReorder;
try {
canReorder = model.canReorder(object);
} catch (UnknownTypeException ex) {
if (!(object instanceof String)) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
canReorder = false;
}
if (canReorder) {
Index i = new IndexImpl(model, object);
indexPtr[0] = i;
return Lookups.fixed(object, cnc, i);
} else {
return Lookups.fixed(object, cnc);
}
}
private static Map createColumnIDsMap(ColumnModel[] columns) {
Map cids = null;
for (ColumnModel cm : columns) {
if (cm instanceof HyperColumnModel) {
if (cids == null) {
cids = new HashMap();
}
HyperColumnModel hcm = (HyperColumnModel) cm;
String mainID = cm.getID();
for (String id : hcm.getAllIDs()) {
cids.put(id, mainID);
}
}
}
return cids;
}
private boolean areChildrenInitialized() {
if (lazyChildren != null) {
return lazyChildren.areChildrenCreated();
} else {
return true;
}
}
private void setIndexWatcher(Index childrenIndex) {
childrenIndex.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (!areChildrenInitialized()) {
return ;
}
Children ch = getChildren();
if (ch instanceof TreeModelChildren) {
((TreeModelChildren) ch).refreshChildren(new TreeModelChildren.RefreshingInfo(false));
}
}
});
}
private static Executor asynchronous(Models.CompoundModel model, CALL asynchCall, Object object) {
Executor exec;
try {
exec = model.asynchronous(asynchCall, object);
//System.err.println("Asynchronous("+asynchCall+", "+object+") = "+exec);
if (exec == null) {
Exceptions.printStackTrace(Exceptions.attachMessage(new NullPointerException("Provided executor is null."), "model = "+model+", object = "+object));
exec = AsynchronousModelFilter.CURRENT_THREAD;
}
} catch (Exception ex) {
Exceptions.printStackTrace(Exceptions.attachMessage(ex, "model = "+model+", object = "+object));
exec = AsynchronousModelFilter.CURRENT_THREAD;
}
return exec;
}
// Node implementation .....................................................
@Override
protected Sheet createSheet() {
Sheet sheet = Sheet.createDefault();
Sheet.Set ps = Sheet.createPropertiesSet ();
int i, k = columns.length;
for (i = 0; i < k; i++)
ps.put (new MyProperty (columns [i], treeModelRoot));
sheet.put (ps);
return sheet;
}
private static Children createChildren (
Models.CompoundModel model,
ColumnModel[] columns,
TreeModelRoot treeModelRoot,
Object object
) {
if (object == null)
throw new NullPointerException ();
try {
return model.isLeaf (object) ?
Children.LEAF :
new TreeModelChildren (model, columns, treeModelRoot, object);
} catch (UnknownTypeException e) {
if (!(object instanceof String)) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
}
return Children.LEAF;
}
}
@Override
public String getShortDescription () {
synchronized (shortDescriptionLock) {
if (shortDescription != null) {
return shortDescription;
}
}
Executor exec = asynchronous(model, CALL.SHORT_DESCRIPTION, object);
if (exec == AsynchronousModelFilter.CURRENT_THREAD) {
return updateShortDescription();
} else {
exec.execute(new Runnable() {
public void run() {
updateShortDescription();
fireShortDescriptionChange(null, null);
}
});
return EVALUATING_STR;
}
}
private String updateShortDescription() {
try {
String sd = model.getShortDescription (object);
if (sd != null) {
sd = adjustHTML(sd);
}
synchronized (shortDescriptionLock) {
shortDescription = sd;
}
return sd;
} catch (UnknownTypeException e) {
if (!(object instanceof String)) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
}
return null;
}
}
private void doFireShortDescriptionChange() {
synchronized (shortDescriptionLock) {
shortDescription = null;
}
fireShortDescriptionChange(null, null);
}
@Override
public String getHtmlDisplayName () {
synchronized (displayNameLock) {
// Compute the HTML display name if the ordinary display name is not available (e.g. was reset)
if (displayName == null) {
try {
setModelDisplayName();
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
}
if (displayName == null) {
displayName = ""; // display name was computed
}
return htmlDisplayName;
}
}
@Override
public Action[] getActions (boolean context) {
Action[] actions;
if (context)
actions = treeModelRoot.getRootNode ().getActions (false);
try {
actions = filterActionsWhenSorted(model.getActions (object));
} catch (UnknownTypeException e) {
// NodeActionsProvider is voluntary
actions = new Action [0];
}
presetActionNodes(actions);
return actions;
}
private void presetActionNodes(Action[] actions) {
for (Action a : actions) {
if (a instanceof ActionOnPresetNodes) {
((ActionOnPresetNodes) a).addNode(this);
}
}
}
private boolean isTableSorted() {
TableColumnModel tcm = treeModelRoot.getOutlineView().getOutline().getColumnModel();
Enumeration cen = tcm.getColumns();
while (cen.hasMoreElements()) {
ETableColumn etc = (ETableColumn) cen.nextElement();
if (etc.isSorted()) {
return true;
}
}
return false;
}
private Action[] filterActionsWhenSorted(Action[] actions) {
if (actions == null || actions.length == 0) {
return actions;
}
for (int i = 0; i < actions.length; i++) {
Action a = actions[i];
if (a == null) continue;
boolean disabled = Boolean.TRUE.equals(a.getValue("DisabledWhenInSortedTable")); // NOI18N
if (disabled) {
if (a instanceof DisableableAction) {
actions[i] = ((DisableableAction) a).createDisableable(new PrivilegedAction() {
@Override
public Object run() {
// Disabled when the table is sorted:
return !isTableSorted();
}
});
} else {
actions[i] = new DisabledWhenSortedAction(a);
}
}
}
return actions;
}
@Override
public Action getPreferredAction () {
return new AbstractAction () {
public void actionPerformed (ActionEvent e) {
try {
model.performDefaultAction (object);
} catch (UnknownTypeException ex) {
// NodeActionsProvider is voluntary
}
}
};
}
@Override
public boolean canDestroy () {
try {
Action[] as = model.getActions (object);
int i, k = as.length;
for (i = 0; i < k; i++) {
if (as [i] == null) continue;
Object key = as [i].getValue (Action.ACCELERATOR_KEY);
if ( (key != null) &&
(key.equals (KeyStroke.getKeyStroke ("DELETE")))
) return as [i].isEnabled ();
}
return false;
} catch (UnknownTypeException e) {
// NodeActionsProvider is voluntary
return false;
}
}
@Override
public boolean canCopy () {
try {
return model.canCopy(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
return false;
}
}
@Override
public boolean canCut () {
try {
return model.canCut(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
return false;
}
}
@Override
public void destroy () {
try {
Action[] as = model.getActions (object);
int i, k = as.length;
for (i = 0; i < k; i++) {
if (as [i] == null) continue;
Object key = as [i].getValue (Action.ACCELERATOR_KEY);
if ( (key != null) &&
(key.equals (KeyStroke.getKeyStroke ("DELETE")))
) {
as [i].actionPerformed (null);
return;
}
}
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
}
if (model.getRoot() == object) {
treeModelRoot.destroy();
}
}
// other methods ...........................................................
void setObject (Object o) {
setObject(model, o);
}
void setObject (Models.CompoundModel model, Object o) {
setObjectNoRefresh (o);
refresh (model);
}
private void setObjectNoRefresh (Object o) {
object = o;
if (areChildrenInitialized()) {
Children ch = getChildren ();
if (ch instanceof TreeModelChildren)
((TreeModelChildren) ch).object = o;
}
}
public Object getObject () {
return object;
}
Models.CompoundModel getModel() {
return model;
}
private Task refreshTask;
private final Object refreshTaskLock = new Object();
private final Set childrenRefreshModels = new HashSet();
void refresh (Models.CompoundModel model) {
//System.err.println("TreeModelNode.refresh("+model+") on "+object);
//Thread.dumpStack();
// 1) empty cache
synchronized (properties) {
properties.clear();
}
// 2) refresh name, displayName and iconBase
synchronized (childrenRefreshModels) {
childrenRefreshModels.add(model);
}
synchronized (refreshTaskLock) {
if (refreshTask == null) {
refreshTask = getRequestProcessor ().create (new Runnable () {
public void run () {
if (!SwingUtilities.isEventDispatchThread()) {
try {
SwingUtilities.invokeAndWait(this);
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
return ;
}
refreshNode ();
doFireShortDescriptionChange();
// 3) refresh children
Set modelsToRefresh;
synchronized (childrenRefreshModels) {
modelsToRefresh = new HashSet(childrenRefreshModels);
childrenRefreshModels.clear();
}
if (modelsToRefresh.size() > 0) {
refreshTheChildren(modelsToRefresh, new TreeModelChildren.RefreshingInfo(true));
}
}
});
}
refreshTask.schedule(10);
}
}
void refresh (final Models.CompoundModel model, int changeMask) {
if (changeMask == 0xFFFFFFFF) {
refresh(model);
return ;
}
boolean refreshed = false;
if ((ModelEvent.NodeChanged.DISPLAY_NAME_MASK & changeMask) != 0) {
boolean doFireDisplayNameChange;
synchronized (displayNameLock) {
doFireDisplayNameChange = displayName != null;
displayName = null;
}
if (doFireDisplayNameChange) {
fireDisplayNameChange(null, null);
}
refreshed = true;
}
if ((ModelEvent.NodeChanged.ICON_MASK & changeMask) != 0) {
if (iconLoaded) {
iconLoaded = false;
fireIconChange();
//fireOpenedIconChange(); - not necessary, just adds more events!
// VisualizerNode.propertyChange() interprets all name/icon changes as one kind.
}
refreshed = true;
}
if ((ModelEvent.NodeChanged.SHORT_DESCRIPTION_MASK & changeMask) != 0) {
doFireShortDescriptionChange();
refreshed = true;
}
if ((ModelEvent.NodeChanged.CHILDREN_MASK & changeMask) != 0) {
boolean doRefresh;
synchronized (childrenRefreshModels) {
doRefresh = childrenRefreshModels.add(model);
}
if (doRefresh) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
synchronized (childrenRefreshModels) {
childrenRefreshModels.remove(model);
}
refreshTheChildren(Collections.singleton(model), new TreeModelChildren.RefreshingInfo(false));
}
});
}
refreshed = true;
}
if ((ModelEvent.NodeChanged.EXPANSION_MASK & changeMask) != 0) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
expandIfSetToExpanded();
}
});
}
if (!refreshed) {
refresh(model);
}
}
private static RequestProcessor requestProcessor;
// Accessed from test
RequestProcessor getRequestProcessor () {
/*RequestProcessor rp = treeModelRoot.getRequestProcessor();
if (rp != null) {
return rp;
}*/
synchronized (TreeModelNode.class) {
if (requestProcessor == null)
requestProcessor = new RequestProcessor ("TreeModel", 1);
return requestProcessor;
}
}
private boolean setName (String name, boolean italics) {
// XXX HACK: HTMLDisplayName is missing in the models!
synchronized (displayNameLock) {
String oldHtmlDisplayName = htmlDisplayName;
String _oldDisplayName = oldDisplayName;
String newDisplayName;
if (name.startsWith (HTML_START_TAG)) {
htmlDisplayName = name;
newDisplayName = removeHTML(name);
} else if (name.startsWith ("<_html>")) { //[TODO] use empty string as name in the case of <_html> tag
htmlDisplayName = '<' + name.substring(2);
newDisplayName = "";
} else {
htmlDisplayName = null;
newDisplayName = name;
}
displayName = newDisplayName;
oldDisplayName = newDisplayName;
return _oldDisplayName == null || !_oldDisplayName.equals(newDisplayName) ||
oldHtmlDisplayName == null || !oldHtmlDisplayName.equals(htmlDisplayName);
}
}
private String parseDisplayFormat(String name) {
MessageFormat treeNodeDisplayFormat = treeModelRoot.getTreeNodeDisplayFormat();
if (treeNodeDisplayFormat == null) {
return name;
}
if (propertyDisplayNameListener == null) {
propertyDisplayNameListener = new PropertyDisplayNameListener();
addPropertyChangeListener(propertyDisplayNameListener);
}
Property>[] nodeProperties = getPropertySets()[0].getProperties();
Format[] formatsByArgumentIndex = treeNodeDisplayFormat.getFormatsByArgumentIndex();
String pattern = treeNodeDisplayFormat.toPattern();
int n = formatsByArgumentIndex.length;
Object[] args = new Object[n];
String[] argsHTML = new String[n];
boolean nonEmptyArgs = false;
for (int i = 0; i < n; i++) {
if (pattern.indexOf("{"+i) >= 0) {
//if (formatsByArgumentIndex[i] != null) {
if (columns[i].getType() == null) {
if (name.startsWith (HTML_START_TAG)) {
argsHTML[i] = name;
args[i] = removeHTML(name);
} else if (name.startsWith ("<_html>")) {
argsHTML[i] = '<' + name.substring(2);
args[i] = removeHTML((String) argsHTML[i]);
} else {
argsHTML[i] = null;
args[i] = name;
}
} else {
try {
args[i] = nodeProperties[i].getValue();
argsHTML[i] = (String) nodeProperties[i].getValue("htmlDisplayValue");
if (!"".equals(args[i])) {
propertyDisplayNameListener.addPropertyName(nodeProperties[i].getName());
nonEmptyArgs = true;
}
} catch (IllegalAccessException ex) {
Exceptions.printStackTrace(ex);
} catch (InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
}
} else {
args[i] = null;
}
}
if (nonEmptyArgs) {
boolean isHTML = false;
int iHTML = -1;
for (int i = 0; i < n; i++) {
if (argsHTML[i] != null) {
isHTML = true;
iHTML = i;
args[i] = stripHTMLTags(argsHTML[i]);
} else if (isHTML && args[i] instanceof String) {
args[i] = adjustHTML((String) args[i]);
}
}
for (int i = 0; i < iHTML; i++) {
if (args[i] instanceof String) {
args[i] = adjustHTML((String) args[i]);
}
}
String format = treeNodeDisplayFormat.format(args);
if (isHTML) {
format = HTML_START_TAG+format+HTML_END_TAG;
}
return format; //new Object[] { name });
} else {
return name;
}
}
private static String stripHTMLTags(String str) {
if (str.startsWith(HTML_START_TAG)) {
str = str.substring(HTML_START_TAG.length());
}
if (str.endsWith(HTML_END_TAG)) {
str = str.substring(0, str.length() - HTML_END_TAG.length());
}
return str;
}
private PropertyDisplayNameListener propertyDisplayNameListener;
private class PropertyDisplayNameListener implements PropertyChangeListener {
private Set propertyNames = new HashSet();
PropertyDisplayNameListener() {
}
void addPropertyName(String propertyName) {
propertyNames.add(propertyName);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (propertyNames.contains(evt.getPropertyName())) {
try {
setModelDisplayName();
fireDisplayNameChange(null, null);
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
}
}
}
private void setModelDisplayName() throws UnknownTypeException {
Executor exec = asynchronous(model, CALL.DISPLAY_NAME, object);
if (exec == AsynchronousModelFilter.CURRENT_THREAD) {
String name = model.getDisplayName (object);
if (name == null) {
Throwable t =
new NullPointerException (
"Model: " + model + ".getDisplayName (" + object +
") = null!"
);
Exceptions.printStackTrace(t);
} else {
name = parseDisplayFormat(name);
setName (name, false);
}
} else {
final String originalDisplayName = (oldDisplayName != null) ? oldDisplayName : "";
setName(EVALUATING_STR, false);
exec.execute(new Runnable() {
public void run() {
String name;
try {
name = model.getDisplayName(object);
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
setName(originalDisplayName, false);
fireDisplayNameChange(null, originalDisplayName);
return ;
}
if (name == null) {
Throwable t =
new NullPointerException (
"Model: " + model + ".getDisplayName (" + object +
") = null!"
);
Exceptions.printStackTrace(t);
setName(originalDisplayName, false);
fireDisplayNameChange(null, originalDisplayName);
} else {
if (setName (name, false)) {
fireDisplayNameChange(null, name);
}
}
}
});
}
}
@Override
public String getDisplayName() {
synchronized (displayNameLock) {
if (displayName == null) {
try {
setModelDisplayName();
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
}
if (displayName == null) {
displayName = "";
}
return displayName;
}
}
@Override
public void setDisplayName(String s) {
String sOld;
synchronized (displayNameLock) {
if ((displayName != null) && displayName.equals(s)) {
return ;
}
sOld = displayName;
displayName = oldDisplayName = s;
}
fireDisplayNameChange(sOld, s);
}
private void setModelIcon() throws UnknownTypeException {
String iconBase = null;
if (model.getRoot() != object) {
iconBase = model.getIconBaseWithExtension (object);
}
if (iconBase != null)
setIconBaseWithExtension (iconBase);
else
setIconBaseWithExtension ("org/openide/resources/actions/empty.gif");
}
@Override
public Image getIcon(int type) {
if (!iconLoaded) {
try {
setModelIcon();
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
iconLoaded = true;
}
return super.getIcon(type);
}
@Override
public Image getOpenedIcon(int type) {
if (!iconLoaded) {
try {
setModelIcon();
} catch (UnknownTypeException ex) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, ex);
}
iconLoaded = true;
}
return super.getOpenedIcon(type);
}
private void refreshNode () {
boolean doFireDisplayNameChange;
synchronized (displayNameLock) {
doFireDisplayNameChange = displayName != null;
displayName = null;
}
if (doFireDisplayNameChange) {
fireDisplayNameChange(null, null);
}
if (iconLoaded) {
iconLoaded = false;
fireIconChange();
//fireOpenedIconChange(); - not necessary, just adds more events!
// VisualizerNode.propertyChange() interprets all name/icon changes as one kind.
}
firePropertyChange(null, null, null);
}
void refreshColumn(String column, int changeMask) {
String visualColumn = column;
if (columnIDsMap != null) {
String c = columnIDsMap.get(column);
if (c != null) {
visualColumn = c;
}
}
synchronized (properties) {
if ((ModelEvent.TableValueChanged.VALUE_MASK & changeMask) != 0) {
properties.remove(column);
}
if ((ModelEvent.TableValueChanged.HTML_VALUE_MASK & changeMask) != 0) {
properties.remove(column + "#html");
}
if ((ModelEvent.TableValueChanged.IS_READ_ONLY_MASK & changeMask) != 0) {
properties.remove(column + "#canWrite");
}
}
firePropertyChange(visualColumn, null, null);
}
/**
* @param model The associated model - necessary for hyper node.
* @param refreshSubNodes If recursively refresh subnodes.
*/
protected void refreshTheChildren(Set models, TreeModelChildren.RefreshingInfo refreshInfo) {
for (Models.CompoundModel model: models) {
refreshTheChildren(model, refreshInfo);
}
}
/**
* @param model The associated model - necessary for hyper node.
* @param refreshSubNodes If recursively refresh subnodes.
*/
private void refreshTheChildren(Models.CompoundModel model, TreeModelChildren.RefreshingInfo refreshInfo) {
if (!areChildrenInitialized()) {
return ;
}
Children ch = getChildren();
try {
if (ch instanceof TreeModelChildren) {
if (model.isLeaf(object)) {
setChildren(Children.LEAF);
} else {
((TreeModelChildren) ch).refreshChildren(refreshInfo);
}
} else if (!model.isLeaf (object)) {
setChildren(new TreeModelChildren (model, columns, treeModelRoot, object));
}
} catch (UnknownTypeException utex) {
// not known - do not change children
if (!(object instanceof String)) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, utex);
}
setChildren(Children.LEAF);
}
}
private static String htmlValue (String name) {
if (!(name.length() > 6 && name.substring(0, 6).equalsIgnoreCase(HTML_START_TAG))) return null;
if (name.length() > MAX_HTML_LENGTH) {
int endTagsPos = findEndTagsPos(name);
String ending = name.substring(endTagsPos + 1);
name = name.substring(0, MAX_HTML_LENGTH - 3 - ending.length());
// Check whether we haven't cut "&...;" in between:
int n = name.length();
for (int i = n - 1; i > n - 6; i--) {
if (name.charAt(i) == ';') {
break; // We have an end of the group
}
if (name.charAt(i) == '&') {
name = name.substring(0, i);
break;
}
}
name += "..." + ending;
}
return adjustHTML(name);
}
private static int findEndTagsPos(String s) {
int openings = 0;
int i;
for (i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) == '>') openings++;
else if (s.charAt(i) == '<') openings--;
else if (openings == 0) break;
}
return i;
}
private static String removeHTML (String text) {
if (!(text.length() > 6 && text.substring(0, 6).equalsIgnoreCase(HTML_START_TAG))) {
return text;
}
text = text.replace ("", "")
.replace ("", "")
.replace ("", "")
.replace ("", "")
.replace (HTML_START_TAG, "")
.replace (HTML_END_TAG, "")
.replace ("", "");
int i = text.indexOf ("= 0) {
int j = text.indexOf (">", i);
text = text.substring (0, i) + text.substring (j + 1);
i = text.indexOf ("")
.replace ("&", "&");
}
/** Adjusts HTML text so that it's rendered correctly.
* In particular, this assures that white characters are visible.
*/
private static String adjustHTML(String text) {
text = text.replace("\\", "\\\\");
StringBuffer sb = null;
int j = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
String replacement = null;
if (c == '\n') {
replacement = "\\n";
} else if (c == '\r') {
replacement = "\\r";
} else if (c == '\f') {
replacement = "\\f";
} else if (c == '\b') {
replacement = "\\b";
}
if (replacement != null) {
if (sb == null) {
sb = new StringBuffer(text.substring(0, i));
} else {
sb.append(text.substring(j, i));
}
sb.append(replacement);
j = i+1;
}
}
if (sb == null) {
return text;
} else {
sb.append(text.substring(j));
return sb.toString();
}
}
@Override
public boolean canRename() {
try {
return model.canRename(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
return false;
}
}
@Override
public void setName(String s) {
try {
model.setName(object, s);
super.setName(s);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
}
}
@Override
public Transferable clipboardCopy() throws IOException {
Transferable t;
try {
t = model.clipboardCopy(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
t = null;
}
if (t == null) {
return super.clipboardCopy();
} else {
return t;
}
}
@Override
public Transferable clipboardCut() throws IOException {
Transferable t;
try {
t = model.clipboardCut(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
t = null;
}
if (t == null) {
return super.clipboardCut();
} else {
return t;
}
}
@Override
public Transferable drag() throws IOException {
Transferable t;
try {
t = model.drag(object);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
t = null;
}
if (t == null) {
return super.drag();
} else {
return t;
}
}
@Override
public void createPasteTypes(Transferable t, List l) {
PasteType[] p;
try {
p = model.getPasteTypes(object, t);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
p = null;
}
if (p == null) {
super.createPasteTypes(t, l);
} else {
l.addAll(Arrays.asList(p));
}
}
@Override
public PasteType getDropType(Transferable t, int action, int index) {
PasteType p;
try {
p = model.getDropType(object, t, action, index);
} catch (UnknownTypeException e) {
Logger.getLogger(TreeModelNode.class.getName()).log(Level.CONFIG, "Model: "+model, e);
p = null;
}
if (p == null) {
return super.getDropType(t, action, index);
} else {
return p;
}
}
private final void expandIfSetToExpanded() {
try {
DefaultTreeExpansionManager.get(model).setChildrenToActOn(getTreeDepth());
if (model.isExpanded (object)) {
TreeFeatures treeTable = treeModelRoot.getTreeFeatures ();
if (treeTable != null) {
treeTable.expandNode (object);
}
}
} catch (UnknownTypeException ex) {
}
}
private Integer depth;
private Integer getTreeDepth() {
Node p = getParentNode();
if (p == null) {
return 0;
} else if (depth != null) {
return depth;
} else {
int d = 1;
while ((p = p.getParentNode()) != null) d++;
depth = Integer.valueOf(d);
return depth;
}
}
// innerclasses ............................................................
public static interface DisableableAction extends Action {
Action createDisableable(PrivilegedAction enabledTest);
}
/**
* An action, that can act on a pre-set set of nodes.
*/
public static interface ActionOnPresetNodes extends Action {
/**
* Add a node to act on.
* The set of nodes is cleared in the next cycle of event dispatch loop.
* When no nodes are provided, the TopComponent.getRegistry ().getActivatedNodes () are used.
* @param n a node to act on
*/
void addNode(Node n);
}
private class DisabledWhenSortedAction implements Action {
private Action a;
public DisabledWhenSortedAction(Action a) {
this.a = a;
}
@Override
public Object getValue(String key) {
return a.getValue(key);
}
@Override
public void putValue(String key, Object value) {
a.putValue(key, value);
}
@Override
public void setEnabled(boolean b) {
a.setEnabled(b);
}
@Override
public boolean isEnabled() {
if (isTableSorted()) {
return false;
} else {
return a.isEnabled();
}
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
a.addPropertyChangeListener(listener);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
a.removePropertyChangeListener(listener);
}
@Override
public void actionPerformed(ActionEvent e) {
a.actionPerformed(e);
}
}
private static final class CheckNodeCookieImpl implements CheckableNode {
private final Models.CompoundModel model;
private final Object object;
public CheckNodeCookieImpl(Models.CompoundModel model, Object object) {
this.model = model;
this.object = object;
}
public boolean isCheckable() {
try {
return model.isCheckable(object);
} catch (UnknownTypeException ex) {
Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Model = "+model));
return false;
}
}
public boolean isCheckEnabled() {
try {
return model.isCheckEnabled(object);
} catch (UnknownTypeException ex) {
Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Model = "+model));
return false;
}
}
public Boolean isSelected() {
try {
return model.isSelected(object);
} catch (UnknownTypeException ex) {
Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Model = "+model));
return false;
}
}
public void setSelected(Boolean selected) {
try {
model.setSelected(object, selected);
} catch (UnknownTypeException ex) {
Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Model = "+model));
}
}
}
/** Special locals subnodes (children) */
static class TreeModelChildren extends Children.Keys
© 2015 - 2025 Weber Informatics LLC | Privacy Policy