/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.rcp.util.internal;
import com.bc.ceres.core.Assert;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.Mask;
import org.esa.snap.core.datamodel.Mask.ImageType;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductNode;
import org.esa.snap.core.datamodel.ProductNodeGroup;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.core.datamodel.TiePointGrid;
import org.esa.snap.core.datamodel.VectorDataNode;
import org.esa.snap.core.datamodel.VirtualBand;
import org.esa.snap.netbeans.docwin.WindowUtilities;
import org.esa.snap.rcp.util.Dialogs;
import org.esa.snap.rcp.windows.ProductSceneViewTopComponent;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
/**
* Confirms Raster Data Node deletion by the user and performs them.
*
* @author Marco Zuehlke
* @version $Revision$ $Date$
* @since BEAM 4.7
*/
public class RasterDataNodeDeleter {
private static final String INDENT = " ";
public static void deleteVectorDataNode(VectorDataNode vectorDataNode) {
Assert.notNull(vectorDataNode);
Product product = vectorDataNode.getProduct();
ProductNodeGroup<Mask> maskGroup = product.getMaskGroup();
Mask vectorMask = null;
for (int i = 0; i < maskGroup.getNodeCount(); i++) {
Mask mask = maskGroup.get(i);
if (mask.getImageType() == Mask.VectorDataType.INSTANCE &&
Mask.VectorDataType.getVectorData(mask) == vectorDataNode) {
vectorMask = mask;
break;
}
}
String message;
if (vectorMask != null) {
List<RasterDataNode> virtualBands = getReferencedVirtualBands(vectorMask);
List<RasterDataNode> validMaskNodes = getReferencedValidMasks(vectorMask);
List<RasterDataNode> masks = getReferencedMasks(vectorMask);
VectorDataNode[] nodes = new VectorDataNode[]{vectorDataNode};
message = formatPromptMessage("Geometry", nodes, virtualBands, validMaskNodes, masks);
} else {
message = MessageFormat.format("Do you really want to delete the geometry ''{0}''?\nThis action cannot be undone.\n\n", vectorDataNode.getName());
}
final Dialogs.Answer answer = Dialogs.requestDecision("Delete Vector Data", message, true, null);
if (answer == Dialogs.Answer.YES) {
product.getVectorDataGroup().remove(vectorDataNode);
}
}
public static void deleteRasterDataNodes(RasterDataNode[] rasterNodes) {
Assert.notNull(rasterNodes);
if (rasterNodes.length == 0) {
return;
}
Set<RasterDataNode> virtualBandsSet = new HashSet<RasterDataNode>();
Set<RasterDataNode> validMaskNodesSet = new HashSet<RasterDataNode>();
Set<RasterDataNode> masksSet = new HashSet<RasterDataNode>();
for (RasterDataNode raster : rasterNodes) {
virtualBandsSet.addAll(getReferencedVirtualBands(raster));
validMaskNodesSet.addAll(getReferencedValidMasks(raster));
masksSet.addAll(getReferencedMasks(raster));
}
for (RasterDataNode raster : rasterNodes) {
virtualBandsSet.remove(raster);
validMaskNodesSet.remove(raster);
masksSet.remove(raster);
}
String typeName = getTypeName(rasterNodes);
String message = formatPromptMessage(typeName, rasterNodes, virtualBandsSet, validMaskNodesSet, masksSet);
deleteRasterDataNodesImpl(rasterNodes, message);
}
public static void deleteRasterDataNode(RasterDataNode raster) {
Assert.notNull(raster);
List<RasterDataNode> virtualBands = getReferencedVirtualBands(raster);
List<RasterDataNode> validMaskNodes = getReferencedValidMasks(raster);
List<RasterDataNode> masks = getReferencedMasks(raster);
RasterDataNode[] rasters = new RasterDataNode[]{raster};
String typeName = getTypeName(rasters);
String message = formatPromptMessage(typeName, rasters, virtualBands, validMaskNodes, masks);
deleteRasterDataNodesImpl(rasters, message);
}
private static void deleteRasterDataNodesImpl(RasterDataNode[] rasters, String message) {
final Dialogs.Answer answer = Dialogs.requestDecision("Delete Raster Data", message, true, null);
if (answer == Dialogs.Answer.YES) {
for (RasterDataNode raster : rasters) {
WindowUtilities.getOpened(ProductSceneViewTopComponent.class).
filter(topComponent -> raster == topComponent.getView().getRaster()).
forEach(new Consumer<ProductSceneViewTopComponent>() {
@Override
public void accept(ProductSceneViewTopComponent productSceneViewTopComponent) {
productSceneViewTopComponent.close();
}
});
if (raster.hasRasterData()) {
raster.unloadRasterData();
}
final Product product = raster.getProduct();
if (raster instanceof Mask) {
Mask mask = (Mask) raster;
product.getMaskGroup().remove(mask);
for (Band band : product.getBands()) {
deleteMaskFromGroup(band.getOverlayMaskGroup(), mask);
}
TiePointGrid[] tiePointGrids = product.getTiePointGrids();
for (TiePointGrid tiePointGrid : tiePointGrids) {
deleteMaskFromGroup(tiePointGrid.getOverlayMaskGroup(), mask);
}
ImageType imageType = mask.getImageType();
if (imageType == Mask.VectorDataType.INSTANCE) {
VectorDataNode vectorDataNode = Mask.VectorDataType.getVectorData(mask);
product.getVectorDataGroup().remove(vectorDataNode);
}
} else if (raster instanceof Band) {
product.removeBand((Band) raster);
} else if (raster instanceof TiePointGrid) {
product.removeTiePointGrid((TiePointGrid) raster);
}
}
}
}
private static String formatPromptMessage(String description, ProductNode[] nodes,
Collection<RasterDataNode> virtualBands, Collection<RasterDataNode> validMaskNodes,
Collection<RasterDataNode> masks) {
String name;
StringBuilder message = new StringBuilder();
if ((nodes.length > 1)) {
message.append(MessageFormat.format("Do you really want to delete the following {0}:\n", description));
for (ProductNode node : nodes) {
message.append(INDENT);
message.append(node.getName());
message.append("\n");
}
} else {
name = nodes[0].getName();
message.append(MessageFormat.format("Do you really want to delete the {0} ''{1}''?\n", description, name));
}
message.append("This action cannot be undone.\n\n");
if (!virtualBands.isEmpty()
|| !validMaskNodes.isEmpty()
|| !masks.isEmpty()) {
if ((nodes.length > 1)) {
message.append(MessageFormat.format("The {0} to be deleted are referenced by\n", description));
} else {
message.append(MessageFormat.format("The {0} to be deleted is referenced by\n", description));
}
}
if (!virtualBands.isEmpty()) {
message.append("the expression of virtual band(s):\n");
for (RasterDataNode virtualBand : virtualBands) {
message.append(INDENT);
message.append(virtualBand.getName());
message.append("\n");
}
}
if (!validMaskNodes.isEmpty()) {
message.append("the valid-mask expression of band(s) or tie-point grid(s)\n");
for (RasterDataNode validMaskNode : validMaskNodes) {
message.append(INDENT);
message.append(validMaskNode.getName());
message.append("\n");
}
}
if (!masks.isEmpty()) {
message.append("the mask(s):\n");
for (RasterDataNode mask : masks) {
message.append(INDENT);
message.append(mask.getName());
message.append("\n");
}
}
return message.toString();
}
private static String getTypeName(RasterDataNode[] rasters) {
String description = "";
if (rasters[0] instanceof Mask) {
description = "mask";
} else if (rasters[0] instanceof Band) {
description = "band";
} else if (rasters[0] instanceof TiePointGrid) {
description = "tie-point grid";
}
if (rasters.length > 1) {
description += "s";
}
return description;
}
private static void deleteMaskFromGroup(ProductNodeGroup<Mask> group, Mask mask) {
if (group.contains(mask)) {
group.remove(mask);
}
}
private static List<RasterDataNode> getReferencedValidMasks(final RasterDataNode node) {
final Product product = node.getProduct();
final List<RasterDataNode> rasterList = new ArrayList<RasterDataNode>();
if (product != null) {
for (int i = 0; i < product.getNumBands(); i++) {
final Band band = product.getBandAt(i);
if (band != node) {
if (isNodeReferencedByExpression(node, band.getValidPixelExpression())) {
rasterList.add(band);
}
}
}
for (int i = 0; i < product.getNumTiePointGrids(); i++) {
final TiePointGrid tiePointGrid = product.getTiePointGridAt(i);
if (tiePointGrid != node) {
if (isNodeReferencedByExpression(node, tiePointGrid.getValidPixelExpression())) {
rasterList.add(tiePointGrid);
}
}
}
}
return rasterList;
}
private static List<RasterDataNode> getReferencedMasks(final RasterDataNode node) {
final Product product = node.getProduct();
final List<RasterDataNode> rasterList = new ArrayList<RasterDataNode>();
if (product != null) {
final ProductNodeGroup<Mask> maskGroup = product.getMaskGroup();
final Mask[] masks = maskGroup.toArray(new Mask[maskGroup.getNodeCount()]);
for (final Mask mask : masks) {
final String expression;
if (mask.getImageType() == Mask.BandMathsType.INSTANCE) {
expression = Mask.BandMathsType.getExpression(mask);
} else if (mask.getImageType() == Mask.RangeType.INSTANCE) {
expression = Mask.RangeType.getRasterName(mask);
} else {
expression = null;
}
if (isNodeReferencedByExpression(node, expression)) {
rasterList.add(mask);
}
}
}
return rasterList;
}
private static List<RasterDataNode> getReferencedVirtualBands(final RasterDataNode node) {
final Product product = node.getProduct();
final List<RasterDataNode> rasterList = new ArrayList<RasterDataNode>();
if (product != null) {
for (int i = 0; i < product.getNumBands(); i++) {
final Band band = product.getBandAt(i);
if (band instanceof VirtualBand) {
final VirtualBand virtualBand = (VirtualBand) band;
if (isNodeReferencedByExpression(node, virtualBand.getExpression())) {
rasterList.add(virtualBand);
}
}
}
}
return rasterList;
}
@SuppressWarnings({"SimplifiableIfStatement"})
private static boolean isNodeReferencedByExpression(RasterDataNode node, String expression) {
if (expression == null || expression.trim().isEmpty()) {
return false;
}
return expression.matches(".*\\b" + node.getName() + "\\b.*");
}
}