Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 1917-MimeTypeTree

This commit is contained in:
William Schaefer 2016-11-29 16:40:00 -05:00
commit a78e3a5bdd
14 changed files with 203 additions and 38 deletions

View File

@ -39,7 +39,7 @@ abstract class TagAddedEvent<T extends Tag> extends AutopsyEvent implements Seri
private transient T tag;
/**
* The id of the tag that was added. This will bu used to re-load the
* The id of the tag that was added. This will be used to re-load the
* transient tag from the database.
*/
private final Long tagID;

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.corecomponents;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.FontMetrics;
@ -26,6 +27,7 @@ import java.awt.dnd.DnDConstants;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@ -41,6 +43,7 @@ import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableCellRenderer;
import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.OutlineView;
@ -66,6 +69,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
//@ServiceProvider(service = DataResultViewer.class)
public class DataResultViewerTable extends AbstractDataResultViewer {
private static final long serialVersionUID = 1L;
private final String firstColumnLabel = NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.firstColLbl");
/* The properties map maps
* key: stored value of column index -> value: property at that index
@ -76,6 +81,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
private final Map<Integer, Property<?>> propertiesMap = new TreeMap<>();
private final DummyNodeListener dummyNodeListener = new DummyNodeListener();
private static final String DUMMY_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.dummyNodeDisplayName");
private static final Color TAGGED_COLOR = new Color(200, 210, 220);
private Node currentRoot;
// When a column in the table is moved, these two variables keep track of where
// the column started and where it ended up.
@ -113,6 +119,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
ov.getOutline().setRootVisible(false);
ov.getOutline().setDragEnabled(false);
// add a listener so that when columns are moved, the new order is stored
ov.getOutline().getColumnModel().addColumnModelListener(new TableColumnModelListener() {
@Override
public void columnAdded(TableColumnModelEvent e) {
@ -183,6 +190,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
}
});
// add a listener to move columns back if user tries to move the first column out of place
ov.getOutline().getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
@ -387,7 +395,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
ov.getOutline().setAutoResizeMode((props.size() > 0) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS);
if (root.getChildren().getNodesCount() != 0) {
final Graphics graphics = ov.getGraphics();
if (graphics != null) {
final FontMetrics metrics = graphics.getFontMetrics();
@ -397,7 +404,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
for (int column = 0; column < ov.getOutline().getModel().getColumnCount(); column++) {
int firstColumnPadding = (column == 0) ? 32 : 0;
int columnWidthLimit = (column == 0) ? 250 : 350;
int columnWidthLimit = (column == 0) ? 350 : 300;
int valuesWidth = 0;
// find the maximum width needed to fit the values for the first 100 rows, at most
@ -421,6 +428,48 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
// if there's no content just auto resize all columns
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
/**
* This custom renderer extends the renderer that was already being
* used by the outline table. This renderer colors a row if the
* tags property of the node is not empty.
*/
class ColorTagCustomRenderer extends DefaultOutlineCellRenderer {
private static final long serialVersionUID = 1L;
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row, int col) {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
// only override the color if a node is not selected
if (!isSelected) {
Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row));
boolean tagFound = false;
if (node != null) {
Node.PropertySet[] propSets = node.getPropertySets();
if (propSets.length != 0) {
// currently, a node has only one property set, named Sheet.PROPERTIES ("properties")
Node.Property<?>[] props = propSets[0].getProperties();
for (Property<?> prop : props) {
if (prop.getName().equals("Tags")) {
try {
tagFound = !prop.getValue().equals("");
} catch (IllegalAccessException | InvocationTargetException ignore) {
}
break;
}
}
}
}
//if the node does have associated tags, set its background color
if (tagFound) {
component.setBackground(TAGGED_COLOR);
}
}
return component;
}
}
ov.getOutline().setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
}
/**
@ -547,11 +596,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
if (SwingUtilities.isEventDispatchThread()) {
setupTable(nme.getNode());
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setupTable(nme.getNode());
}
SwingUtilities.invokeLater(() -> {
setupTable(nme.getNode());
});
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2014 Basis Technology Corp.
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,17 +20,24 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import org.openide.nodes.Children;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -103,9 +110,23 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
// case was closed. Remove listeners so that we don't get called with a stale case handle
removeListeners();
}
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(content)) {
updateSheet();
}
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID() == content.getId()) {
updateSheet();
}
}
};
private void updateSheet() {
this.setSheet(createSheet());
}
// Note: this order matters for the search result, changed it if the order of property headers on the "KeywordSearchNode"changed
public static enum AbstractFilePropertyType {
@ -278,6 +299,24 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(AbstractFilePropertyType.MIMETYPE.toString(), content.getMIMEType() == null ? "" : content.getMIMEType());
}
/**
* Used by subclasses of AbstractAbstractFileNode to add the tags property
* to their sheets.
* @param ss the modifiable Sheet.Set returned by Sheet.get(Sheet.PROPERTIES)
*/
protected void addTagProperty(Sheet.Set ss) {
final String NO_DESCR = NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.addFileProperty.desc");
List<ContentTag> tags;
try {
tags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(content);
} catch (TskCoreException ex) {
tags = new ArrayList<>();
LOGGER.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex);
}
ss.put(new NodeProperty<>("Tags", NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.addFileProperty.tags.displayName"),
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
}
static String getContentDisplayName(AbstractFile file) {
String name = file.getName();
switch (name) {

View File

@ -84,6 +84,9 @@ public abstract class AbstractFsContentNode<T extends AbstractFile> extends Abst
ss.put(new NodeProperty<>(HIDE_PARENT, HIDE_PARENT, HIDE_PARENT, HIDE_PARENT));
}
// add tags property to the sheet
addTagProperty(ss);
return s;
}

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -25,6 +27,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children;
@ -35,6 +38,10 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile;
@ -43,6 +50,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -70,6 +78,39 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),};
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
BlackBoardArtifactTagAddedEvent event = (BlackBoardArtifactTagAddedEvent) evt;
if (event.getAddedTag().getArtifact().equals(artifact)) {
updateSheet();
}
} else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
BlackBoardArtifactTagDeletedEvent event = (BlackBoardArtifactTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
updateSheet();
}
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(associated)) {
updateSheet();
}
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID()== associated.getId()) {
updateSheet();
}
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
removeListeners();
}
}
}
};
/**
* Construct blackboard artifact node from an artifact and using provided
* icon
@ -86,6 +127,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
this.setName(Long.toString(artifact.getArtifactID()));
this.setDisplayName();
this.setIconBaseWithExtension(iconPath);
Case.addPropertyChangeListener(pcl);
}
/**
@ -103,6 +145,11 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
this.setName(Long.toString(artifact.getArtifactID()));
this.setDisplayName();
this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS
Case.addPropertyChangeListener(pcl);
}
private void removeListeners() {
Case.removePropertyChangeListener(pcl);
}
@Override
@ -208,6 +255,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
ss.put(np);
}
}
final int artifactTypeId = artifact.getArtifactTypeID();
// If mismatch, add props for extension and file type
@ -294,9 +342,24 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
}
}
// add properties for tags
List<Tag> tags = new ArrayList<>();
try {
tags.addAll(Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
tags.addAll(Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(associated));
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
}
ss.put(new NodeProperty<>("Tags", NbBundle.getMessage(AbstractAbstractFileNode.class, "BlackboardArtifactNode.createSheet.tags.displayName"),
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
return s;
}
private void updateSheet() {
this.setSheet(createSheet());
}
private String getRootParentName() {
String parentName = associated.getName();
Content parent = associated;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2014 Basis Technology Corp.
* Copyright 2013-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -270,3 +270,8 @@ DeleteReportAction.actionDisplayName.multipleReports=Delete Reports
DeleteReportAction.actionPerformed.showConfirmDialog.title=Confirm Deletion
DeleteReportAction.actionPerformed.showConfirmDialog.single.msg=Do you want to delete 1 report from the case?
DeleteReportAction.actionPerformed.showConfirmDialog.multiple.msg=Do you want to delete {0} reports from the case?
AbstractAbstractFileNode.addFileProperty.desc=no description
AbstractAbstractFileNode.addFileProperty.tags.name=Tags
AbstractAbstractFileNode.addFileProperty.tags.displayName=Tags
BlackboardArtifactNode.createSheet.tags.name=Tags
BlackboardArtifactNode.createSheet.tags.displayName=Tags

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Copyright 2013-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -42,6 +42,8 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
class ContentTagNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS
private final ContentTag tag;
@ -60,7 +62,7 @@ class ContentTagNode extends DisplayableItemNode {
try {
contentPath = content.getUniquePath();
} catch (TskCoreException ex) {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + content.getId() + ")", ex); //NON-NLS
LOGGER.log(Level.SEVERE, "Failed to get path for content (id = " + content.getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.unavail.path");
}
AbstractFile file = content instanceof AbstractFile ? (AbstractFile) content : null;
@ -103,6 +105,7 @@ class ContentTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
"",
content.getSize()));
return propertySheet;
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2014 Basis Technology Corp.
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,6 +23,7 @@ import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
@ -103,7 +104,7 @@ public class DataSourcesNode extends DisplayableItemNode {
currentKeys = Case.getCurrentCase().getDataSources();
setKeys(currentKeys);
} catch (TskCoreException | IllegalStateException ex) {
logger.severe("Error getting data sources: " + ex.getMessage()); // NON-NLS
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
setKeys(Collections.<Content>emptySet());
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2015 Basis Technology Corp.
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -346,7 +346,7 @@ public class KeywordHits implements AutopsyVisitableItem {
public class ListNode extends DisplayableItemNode implements Observer {
private String listName;
private final String listName;
public ListNode(String listName) {
super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
@ -411,7 +411,7 @@ public class KeywordHits implements AutopsyVisitableItem {
private class TermFactory extends ChildFactory.Detachable<String> implements Observer {
private String setName;
private final String setName;
private TermFactory(String setName) {
super();
@ -447,8 +447,8 @@ public class KeywordHits implements AutopsyVisitableItem {
public class TermNode extends DisplayableItemNode implements Observer {
private String setName;
private String keyword;
private final String setName;
private final String keyword;
public TermNode(String setName, String keyword) {
super(Children.create(new HitsFactory(setName, keyword), true), Lookups.singleton(keyword));
@ -509,8 +509,8 @@ public class KeywordHits implements AutopsyVisitableItem {
public class HitsFactory extends ChildFactory.Detachable<Long> implements Observer {
private String keyword;
private String setName;
private final String keyword;
private final String setName;
public HitsFactory(String setName, String keyword) {
super();

View File

@ -86,6 +86,9 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
ss.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
// add tags property to the sheet
addTagProperty(ss);
return s;
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2014 Basis Technology Corp.
* Copyright 2013-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -74,7 +75,9 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
for (Map.Entry<String, Object> entry : map.entrySet()) {
ss.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
// @@@ add more properties here...
// add tags property to the sheet
addTagProperty(ss);
return s;
}
@ -82,9 +85,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
@Override
public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) {
actionsList.add(a);
}
actionsList.addAll(Arrays.asList(super.getActions(true)));
actionsList.add(new ViewContextAction(NbBundle.getMessage(this.getClass(), "LocalFileNode.viewFileInDir.text"), this.content));
actionsList.add(null); // creates a menu separator
actionsList.add(new NewWindowViewAction(

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2015 Basis Technology Corp.
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -325,7 +325,7 @@ public class Tags implements AutopsyVisitableItem {
public class ContentTagTypeNode extends DisplayableItemNode implements Observer {
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private TagName tagName;
private final TagName tagName;
public ContentTagTypeNode(TagName tagName) {
super(Children.create(new ContentTagNodeFactory(tagName), true), Lookups.singleton(tagName.getDisplayName() + " " + CONTENT_DISPLAY_NAME));
@ -423,7 +423,7 @@ public class Tags implements AutopsyVisitableItem {
*/
public class BlackboardArtifactTagTypeNode extends DisplayableItemNode implements Observer {
private TagName tagName;
private final TagName tagName;
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
public BlackboardArtifactTagTypeNode(TagName tagName) {

View File

@ -148,6 +148,7 @@ public class VirtualDirectoryNode extends AbstractAbstractFileNode<VirtualDirect
for (Map.Entry<String, Object> entry : map.entrySet()) {
ss.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
addTagProperty(ss);
} else {
ss.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_type_name(),
Bundle.VirtualDirectoryNode_createSheet_type_displayName(),