/* * 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.views.data.internal.compare; import java.text.MessageFormat; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jface.viewers.BaseLabelProvider; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.core.report.Message; import eu.esdihumboldt.hale.common.instance.extension.validation.report.InstanceValidationMessage; import eu.esdihumboldt.hale.common.instance.extension.validation.report.InstanceValidationReport; import eu.esdihumboldt.hale.common.instance.model.Group; import eu.esdihumboldt.hale.common.instance.model.Instance; import eu.esdihumboldt.hale.common.instancevalidator.InstanceValidator; import eu.esdihumboldt.hale.common.schema.model.ChildDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.common.definition.DefinitionImages; import eu.esdihumboldt.hale.ui.common.definition.viewer.TypeDefinitionContentProvider; /** * Label provider for instances in a tree based on a * {@link TypeDefinitionContentProvider}. Also handles metadata of those * instances. * * @author Simon Templer */ public class DefinitionInstanceLabelProvider extends StyledCellLabelProvider { /** * The pattern of the text for multiple values. This is used by * {@link DefinitionInstanceTreeViewer} to determine if a cell is * "editable". Furthermore it is expected to be at the end of the cell text. */ public static final String MULTIPLE_VALUE_FORMAT = "({0} of {1})"; private static final int MAX_STRING_LENGTH = 200; private final Instance instance; private final DefinitionImages images = new DefinitionImages(); private final Map<LinkedList<Object>, Integer> chosenPaths = new HashMap<LinkedList<Object>, Integer>(); private final Map<Object, Integer> chosenMetaPaths = new HashMap<Object, Integer>(); private final InstanceValidator validator; /** * Create an instance label provider for tree based on a * {@link TypeDefinition} * * @param instance the instance to use */ public DefinitionInstanceLabelProvider(Instance instance) { super(); this.instance = instance; this.validator = InstanceValidator.createDefaultValidator(HaleUI.getServiceProvider()); } @SuppressWarnings("javadoc") public static class InstanceEntry { /** * If a definition is represented by the entry. */ private final boolean definition; /** * The value count of the entry. */ private final int valueCount; /** * The index of the chosen value. */ private final int choice; /** * The actual value of the entry. */ private final Object value; /** * The associated child definition, if any. */ private final ChildDefinition<?> childDef; public InstanceEntry(int valueCount, int choice, Object value, boolean definition, ChildDefinition<?> childDef) { super(); this.valueCount = valueCount; this.choice = choice; this.value = value; this.definition = definition; this.childDef = childDef; } public int getValueCount() { return valueCount; } public int getChoice() { return choice; } public Object getValue() { return value; } public boolean isDefinition() { return definition; } public ChildDefinition<?> getChildDef() { return childDef; } } /** * Find the instance entry at the given tree path. * * @param treePath the tree path * @return the instance entry information */ public InstanceEntry findInstanceEntry(TreePath treePath) { // descend in instance int valueCount = 0; int choice = 0; Object value = instance; ChildDefinition<?> childDef = null; LinkedList<Object> segmentList = new LinkedList<Object>(); boolean definition = false; // if a definition is represented // First segment is TypeDefinition. if (treePath.getFirstSegment() instanceof EntityDefinition) { definition = true; segmentList.add(((EntityDefinition) treePath.getFirstSegment()).getType()); for (int i = 1; value != null && i < treePath.getSegmentCount(); i++) { Object segment = treePath.getSegment(i); if (segment instanceof EntityDefinition) { segment = ((EntityDefinition) segment).getDefinition(); } segmentList.add(segment); if (segment instanceof ChildDefinition<?>) { childDef = (ChildDefinition<?>) segment; Object[] values = ((Group) value).getProperty(childDef.getName()); choice = 0; valueCount = 0; if (values != null && values.length > 0) { Integer chosenPath = chosenPaths.get(segmentList); choice = chosenPath == null ? 0 : chosenPath; value = values[choice]; valueCount = values.length; } else value = null; } else { // TODO log message? value = null; } } } else { // if segments contain a set of metadata keys if (treePath.getFirstSegment() instanceof Set<?>) { if (treePath.getSegmentCount() > 1 && value != null) { Object key = treePath.getLastSegment(); if (key instanceof String) { List<Object> values = ((Instance) value).getMetaData(key.toString()); choice = 0; valueCount = 0; if (values != null && values.size() > 0) { Integer chosenPath = chosenMetaPaths.get(key); choice = chosenPath == null ? 0 : chosenPath; value = values.get(choice); valueCount = values.size(); } else value = null; } } } } return new InstanceEntry(valueCount, choice, value, definition, childDef); } /** * @see CellLabelProvider#update(ViewerCell) */ @Override public void update(ViewerCell cell) { TreePath treePath = cell.getViewerRow().getTreePath(); InstanceEntry entry = findInstanceEntry(treePath); Object value = entry.value; InstanceValidationReport report = null; // If childDef is null we are at the top element. if (entry.definition && entry.childDef == null) { report = validator.validate(instance); } boolean hasValue = false; if (entry.definition && value instanceof Instance) { hasValue = ((Instance) value).getValue() != null; } else if (!entry.definition && treePath.getSegmentCount() == 1) { // metadata root if (instance.getMetaDataNames().isEmpty()) { hasValue = true; value = null; } } StyledString styledString; if (value == null) { styledString = new StyledString("no value", StyledString.DECORATIONS_STYLER); } else if (value instanceof Group && !hasValue) { styledString = new StyledString("+", StyledString.QUALIFIER_STYLER); } else { if (value instanceof Instance) { value = ((Instance) value).getValue(); } // TODO some kind of conversion? String stringValue = value.toString(); /* * Values that are very large, e.g. string representations of very * complex geometries lead to * StyledCellLabelProvider.updateTextLayout taking a very long time, * rendering the application unresponsive when the data views are * displayed. As such, we reduce the string to a maximum size. */ if (stringValue.length() > MAX_STRING_LENGTH) { stringValue = stringValue.substring(0, MAX_STRING_LENGTH) + "..."; } styledString = new StyledString(stringValue, null); } // mark cell if there are other values if (entry.valueCount > 1) { String decoration = " " + MessageFormat.format(MULTIPLE_VALUE_FORMAT, entry.choice + 1, entry.valueCount); styledString.append(decoration, StyledString.COUNTER_STYLER); } cell.setText(styledString.toString()); cell.setStyleRanges(styledString.getStyleRanges()); if (report != null && !report.getWarnings().isEmpty()) cell.setImage(PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_OBJS_WARN_TSK)); super.update(cell); } /** * Select a specific path. * * @param path the path at which a choice is necessary * @param choice the made choice */ public void selectPath(TreePath path, int choice) { LinkedList<Object> segmentList = new LinkedList<Object>(); for (int i = 0; i < path.getSegmentCount(); i++) { Object element = path.getSegment(i); if (element instanceof EntityDefinition) { element = ((EntityDefinition) element).getDefinition(); } segmentList.add(element); } if (path.getFirstSegment() instanceof EntityDefinition) { if (choice == 1) chosenPaths.remove(segmentList); else chosenPaths.put(segmentList, choice - 1); } if (path.getFirstSegment() instanceof Set<?>) { if (choice == 1) chosenMetaPaths.remove(path.getLastSegment()); chosenMetaPaths.put(path.getLastSegment(), choice - 1); } } /** * @see BaseLabelProvider#dispose() */ @Override public void dispose() { images.dispose(); super.dispose(); } /** * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object) */ @Override public String getToolTipText(Object element) { if (element instanceof EntityDefinition) { InstanceValidationReport report = validator.validate(instance); Collection<InstanceValidationMessage> warnings = report.getWarnings(); if (warnings.isEmpty()) return null; StringBuilder toolTip = new StringBuilder(); for (Message m : warnings) toolTip.append(m.getFormattedMessage()).append('\n'); return toolTip.substring(0, toolTip.length() - 1); } else return null; } /** * get the specific choosen metadata value number for a certain metadata key * * @param key the metadata key * @return the choice represented by an int */ public int getMetaDataChoice(String key) { if (chosenMetaPaths.containsKey(key)) { return chosenMetaPaths.get(key).intValue(); } else return 0; } }