/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.ui.common.definition;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.geotools.legend.Drawer;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.opengis.feature.simple.SimpleFeature;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.instance.model.DataSet;
import eu.esdihumboldt.hale.common.schema.Classification;
import eu.esdihumboldt.hale.common.schema.model.Definition;
import eu.esdihumboldt.hale.common.schema.model.GroupPropertyDefinition;
import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.NillableFlag;
import eu.esdihumboldt.hale.common.schema.model.constraint.type.AbstractFlag;
import eu.esdihumboldt.hale.io.xsd.constraint.XmlAttributeFlag;
import eu.esdihumboldt.hale.ui.common.CommonSharedImages;
import eu.esdihumboldt.hale.ui.common.CommonSharedImagesConstants;
import eu.esdihumboldt.hale.ui.common.internal.CommonUIPlugin;
import eu.esdihumboldt.hale.ui.common.service.population.Population;
import eu.esdihumboldt.hale.ui.common.service.population.PopulationService;
import eu.esdihumboldt.hale.ui.common.service.style.StyleService;
import eu.esdihumboldt.hale.ui.geometry.DefaultGeometryUtil;
import eu.esdihumboldt.hale.ui.geometry.service.GeometrySchemaService;
import eu.esdihumboldt.hale.ui.util.swing.SwingRcpUtilities;
/**
* Manages images for definitions. Should be {@link #dispose()}d when the images
* are not used any more.
*
* @author Simon Templer
*/
public class DefinitionImages implements CommonSharedImagesConstants {
// constants for style images
private static final int WIDTH = 16;
private static final int HEIGHT = 16;
private static final int[] LINE_POINTS = new int[] { 0, HEIGHT - 1, WIDTH - 1, 0 };
private static final int[] POLY_POINTS = new int[] { 0, 0, WIDTH - 1, 0, WIDTH - 1, HEIGHT - 1,
0, HEIGHT - 1 };
/**
* Represents a image configuration
*/
private static class ImageConf {
private final String identifier;
private final boolean attribute;
private final boolean def;
private final boolean mandatory;
private final boolean faded;
/**
* Constructor
*
* @param identifier the image identifier/key
* @param attribute if an attribute is represented (not an element)
* @param def if the image is to be marked a default (e.g. default
* geometry)
* @param mandatory if the image is to be marked as mandatory
* @param faded if the image is displayed faded
*/
public ImageConf(String identifier, boolean attribute, boolean def, boolean mandatory,
boolean faded) {
super();
this.identifier = identifier;
this.attribute = attribute;
this.def = def;
this.mandatory = mandatory;
this.faded = faded;
}
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (attribute ? 1231 : 1237);
result = prime * result + (def ? 1231 : 1237);
result = prime * result + (faded ? 1231 : 1237);
result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
result = prime * result + (mandatory ? 1231 : 1237);
return result;
}
/**
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ImageConf other = (ImageConf) obj;
if (attribute != other.attribute)
return false;
if (def != other.def)
return false;
if (faded != other.faded)
return false;
if (identifier == null) {
if (other.identifier != null)
return false;
}
else if (!identifier.equals(other.identifier))
return false;
if (mandatory != other.mandatory)
return false;
return true;
}
}
private final Image attribOverlay = CommonUIPlugin
.getImageDescriptor("/icons/attrib_overlay2.gif").createImage(); //$NON-NLS-1$
private final Image defOverlay = CommonUIPlugin.getImageDescriptor("/icons/def_overlay.gif") //$NON-NLS-1$
.createImage();
private final Image mandatoryOverlay = CommonUIPlugin
.getImageDescriptor("/icons/mandatory_ov2.gif").createImage(); //$NON-NLS-1$
private final Map<ImageConf, Image> overlayedImages = new HashMap<ImageConf, Image>();
private final Map<String, Image> styleImages = new HashMap<String, Image>();
private boolean suppressMandatory = false;
private boolean showStyleLegend = true;
private final ImageHandles handles = new ImageHandles();
/**
* Dispose all images. {@link #getImage(Definition)} may not be called after
* calling this method.
*/
public void dispose() {
for (Image image : overlayedImages.values()) {
image.dispose();
}
overlayedImages.clear();
for (Image image : styleImages.values()) {
image.dispose();
}
styleImages.clear();
attribOverlay.dispose();
defOverlay.dispose();
mandatoryOverlay.dispose();
}
/**
* Get the image for the given definition
*
* @param def the definition
* @return the image, may be <code>null</code>
*/
public Image getImage(Definition<?> def) {
return getImage(null, def);
}
/**
* Get the image for the given entity definition
*
* @param entityDef the entity definition
* @return the image, may be <code>null</code>
*/
public Image getImage(EntityDefinition entityDef) {
return getImage(entityDef, entityDef.getDefinition());
}
/**
* Determines if a type definition has a geometry.
*
* @param type the type definition to test
* @return <code>true</code> if the type has a geometry, false otherwise
*/
protected boolean hasGeometry(TypeDefinition type) {
GeometrySchemaService gss = PlatformUI.getWorkbench()
.getService(GeometrySchemaService.class);
return gss.getDefaultGeometry(type) != null;
}
/**
* Get a legend image for a given type definition
*
* @param type the type definition
* @param dataSet the data set the type definition belongs to
* @param definedOnly if only for defined styles a image shall be created
* @return the legend image or <code>null</code>
*/
protected BufferedImage getLegendImage(TypeDefinition type, DataSet dataSet,
boolean definedOnly) {
StyleService ss = PlatformUI.getWorkbench().getService(StyleService.class);
Style style = (definedOnly) ? (ss.getDefinedStyle(type)) : (ss.getStyle(type, dataSet));
if (style == null) {
return null;
}
// create a dummy feature based on the style
Drawer d = Drawer.create();
SimpleFeature feature = null;
Symbolizer[] symbolizers = SLD.symbolizers(style);
if (symbolizers.length > 0) {
Symbolizer symbolizer = symbolizers[0];
if (symbolizer instanceof LineSymbolizer) {
feature = d.feature(d.line(LINE_POINTS));
}
else if (symbolizer instanceof PointSymbolizer) {
feature = d.feature(d.point(WIDTH / 2, HEIGHT / 2));
}
if (symbolizer instanceof PolygonSymbolizer) {
feature = d.feature(d.polygon(POLY_POINTS));
}
}
if (feature != null) {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
// GraphicsEnvironment.getLocalGraphicsEnvironment().
// getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(WIDTH, HEIGHT,
// Transparency.TRANSLUCENT);
// use white background to have a neutral color even if selected
Color bg = Color.WHITE;
Graphics2D g = image.createGraphics();
try {
g.setColor(bg);
g.fillRect(0, 0, WIDTH, HEIGHT);
} finally {
g.dispose();
}
d.drawDirect(image, feature, style);
return image;
}
return null;
}
/**
* Get the image for the given definition
*
* @param entityDef the entity definition, may be <code>null</code>
* @param def the definition
* @return the image, may be <code>null</code>
*/
protected Image getImage(EntityDefinition entityDef, Definition<?> def) {
Classification clazz = Classification.getClassification(def);
String imageName = getImageForClassification(clazz);
// retrieve image for key
Image image;
if (imageName == null) {
// default
imageName = ISharedImages.IMG_OBJ_ELEMENT;
image = PlatformUI.getWorkbench().getSharedImages().getImage(imageName);
}
else {
image = CommonSharedImages.getImageRegistry().get(imageName);
}
// legend image
// XXX not supported yet
if (def instanceof TypeDefinition
&& !((TypeDefinition) def).getConstraint(AbstractFlag.class).isEnabled()
&& hasGeometry((TypeDefinition) def)) {
TypeDefinition type = (TypeDefinition) def;
DataSet dataSet = DataSet.SOURCE;
// XXX dataSet: how to find out? - does not matter with only using
// defined styles
if (entityDef != null) {
dataSet = DataSet.forSchemaSpace(entityDef.getSchemaSpace());
}
String typeKey = dataSet.name() + "::" + type.getIdentifier();
// XXX check if style image is already there?
// XXX how to handle style changes?
BufferedImage img = getLegendImage(type, dataSet, true);
if (img != null) {
// replace image with style image
ImageData imgData = SwingRcpUtilities.convertToSWT(img);
image = new Image(Display.getCurrent(), imgData);
final Image old;
if (styleImages.containsKey(typeKey)) {
old = styleImages.get(typeKey);
}
else {
old = null;
}
styleImages.put(typeKey, image);
if (old != null) {
// schedule image to be disposed when there are no
// references to it
handles.addReference(image);
}
}
}
// check for inline attributes
else {
boolean attribute = (def instanceof PropertyDefinition)
&& ((PropertyDefinition) def).getConstraint(XmlAttributeFlag.class).isEnabled();
boolean mandatory = false;
if (!suppressMandatory) {
if (def instanceof PropertyDefinition) {
Cardinality cardinality = ((PropertyDefinition) def)
.getConstraint(Cardinality.class);
mandatory = cardinality.getMinOccurs() > 0 && !((PropertyDefinition) def)
.getConstraint(NillableFlag.class).isEnabled();
}
else if (def instanceof GroupPropertyDefinition) {
Cardinality cardinality = ((GroupPropertyDefinition) def)
.getConstraint(Cardinality.class);
mandatory = cardinality.getMinOccurs() > 0;
}
}
boolean deflt = false;
boolean faded = false;
if (entityDef != null) {
// entity definition needed to determine if item is a default
// geometry
deflt = DefaultGeometryUtil.isDefaultGeometry(entityDef);
// and to determine population
PopulationService ps = PlatformUI.getWorkbench()
.getService(PopulationService.class);
if (ps != null && ps.hasPopulation(entityDef.getSchemaSpace())) {
Population pop = ps.getPopulation(entityDef);
faded = (pop != null && pop.getOverallCount() == 0);
}
}
if (deflt || mandatory || attribute || faded) {
// overlayed image
ImageConf conf = new ImageConf(imageName, attribute, deflt, mandatory, faded);
Image overlayedImage = overlayedImages.get(conf);
if (overlayedImage == null) {
// apply overlays to image
Image copy = new Image(image.getDevice(), image.getBounds());
// draw on image
GC gc = new GC(copy);
try {
gc.drawImage(image, 0, 0);
if (attribute) {
gc.drawImage(attribOverlay, 0, 0);
}
if (deflt) {
gc.drawImage(defOverlay, 0, 0);
}
if (mandatory) {
gc.drawImage(mandatoryOverlay, 0, 0);
}
} finally {
gc.dispose();
}
if (faded) {
ImageData imgData = copy.getImageData();
imgData.alpha = 150;
Image copy2 = new Image(image.getDevice(), imgData);
copy.dispose();
copy = copy2;
}
image = copy;
overlayedImages.put(conf, copy);
}
else {
image = overlayedImage;
}
}
}
return image;
}
/**
* Get the image name for the given classification. The image name
* represents a file residing in the icons folder.
*
* @param clazz the classification
* @return the image name or <code>null</code>
*/
private String getImageForClassification(Classification clazz) {
switch (clazz) {
case ABSTRACT_FT:
return IMG_DEFINITION_ABSTRACT_FT;
case CONCRETE_FT:
return IMG_DEFINITION_CONCRETE_FT;
case STRING_PROPERTY:
return IMG_DEFINITION_STRING_PROPERTY;
case NUMERIC_PROPERTY:
return IMG_DEFINITION_NUMERIC_PROPERTY;
case GEOMETRIC_PROPERTY:
return IMG_DEFINITION_GEOMETRIC_PROPERTY;
case GROUP:
return IMG_DEFINITION_GROUP;
case CHOICE:
return IMG_DEFINITION_CHOICE;
case CONCRETE_TYPE:
return IMG_DEFINITION_CONCRETE_TYPE;
case ABSTRACT_TYPE:
return IMG_DEFINITION_ABSTRACT_TYPE;
default:
// where no dedicated image is available yet
return null;
}
}
/**
* @return the suppressMandatory
*/
public boolean isSuppressMandatory() {
return suppressMandatory;
}
/**
* @param suppressMandatory the suppressMandatory to set
*/
public void setSuppressMandatory(boolean suppressMandatory) {
this.suppressMandatory = suppressMandatory;
}
/**
* @return the showStyleLegend
*/
public boolean isShowStyleLegend() {
return showStyleLegend;
}
/**
* @param showStyleLegend the showStyleLegend to set
*/
public void setShowStyleLegend(boolean showStyleLegend) {
this.showStyleLegend = showStyleLegend;
}
}