org.datacleaner.widgets.visualization.JobGraphTransformers.java Source code

Java tutorial

Introduction

Here is the source code for org.datacleaner.widgets.visualization.JobGraphTransformers.java

Source

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.widgets.visualization;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.Set;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;

import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.TruePredicate;
import org.apache.metamodel.schema.Table;
import org.datacleaner.api.AnalyzerResult;
import org.datacleaner.api.InputColumn;
import org.datacleaner.descriptors.ComponentDescriptor;
import org.datacleaner.job.ComponentRequirement;
import org.datacleaner.job.CompoundComponentRequirement;
import org.datacleaner.job.FilterOutcome;
import org.datacleaner.job.HasFilterOutcomes;
import org.datacleaner.job.builder.ComponentBuilder;
import org.datacleaner.user.UserPreferences;
import org.datacleaner.util.GraphUtils;
import org.datacleaner.util.IconUtils;
import org.datacleaner.util.ImageManager;
import org.datacleaner.util.LabelUtils;
import org.datacleaner.util.ReflectionUtils;
import org.datacleaner.util.WidgetUtils;
import org.elasticsearch.common.base.Strings;

import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.Context;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.renderers.EdgeLabelRenderer;

/**
 * Collection of {@link Transformer} (and {@link Predicate} and so on) instances
 * to use in the {@link JobGraph}.
 */
public class JobGraphTransformers {

    public static final String USER_PREFERENCES_PROPERTY_SHOW_CANVAS_HINTS = "JobGraph.ShowCanvasHints";
    public static final String USER_PREFERENCES_PROPERTY_FONT_SIZE_FACTOR = "JobGraph.FontSizeFactor";
    public static final String USER_PREFERENCES_PROPERTY_EDGE_STYLE = "JobGraph.LineStyle";

    public static final String EDGE_STYLE_NAME_STRAIGHT = "straight";
    public static final String EDGE_STYLE_NAME_CURVED = "curved";
    public static final String EDGE_STYLE_NAME_ORTOGHONAL = "orthogonal";

    private static final ImageManager imageManager = ImageManager.get();

    private final UserPreferences _userPreferences;
    private final Set<Object> _highlighedVertexes;

    private final Font _normalFont;
    private final Font _boldFont;

    public JobGraphTransformers(UserPreferences userPreferences, Set<Object> highlighedVertexes) {
        _userPreferences = userPreferences;
        _highlighedVertexes = highlighedVertexes;

        final float fontFactor;
        String fontFactorString = userPreferences.getAdditionalProperties()
                .get(USER_PREFERENCES_PROPERTY_FONT_SIZE_FACTOR);
        if (Strings.isNullOrEmpty(fontFactorString)) {
            fontFactor = 1.0f;
        } else {
            fontFactor = 1.0f * Float.parseFloat(fontFactorString);
        }

        _normalFont = font(WidgetUtils.FONT_SMALL, fontFactor);
        _boldFont = _normalFont.deriveFont(Font.BOLD);
    }

    private Font font(Font font, float fontFactor) {
        if (fontFactor == 1.0) {
            return font;
        }
        return font.deriveFont(font.getSize() * fontFactor);
    }

    public static final Predicate<Context<Graph<Object, JobGraphLink>, JobGraphLink>> EDGE_ARROW_PREDICATE = TruePredicate
            .getInstance();;

    public static final Transformer<JobGraphLink, String> EDGE_LABEL_TRANSFORMER = new Transformer<JobGraphLink, String>() {
        @Override
        public String transform(JobGraphLink link) {
            return link.getLinkLabel();
        }
    };

    public static final Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape> EDGE_ARROW_TRANSFORMER = new Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape>() {
        @Override
        public Shape transform(Context<Graph<Object, JobGraphLink>, JobGraphLink> input) {
            return GraphUtils.ARROW_SHAPE;
        }
    };

    public Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape> getEdgeShapeTransformer() {
        final String edgeStyle = _userPreferences.getAdditionalProperties()
                .get(USER_PREFERENCES_PROPERTY_EDGE_STYLE);
        final Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape> baseTransformer = getBaseEdgeShapeTransformer(
                edgeStyle);

        return new Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape>() {
            @Override
            public Shape transform(Context<Graph<Object, JobGraphLink>, JobGraphLink> input) {
                final Shape result = baseTransformer.transform(input);
                final JobGraphLink link = input.element;
                if (isCompoundRequirementLink(link)) {
                    // make a double link (actually a wedge, but close
                    // enough) to show that there are more than one filter
                    // outcome coming from this source
                    return new EdgeShape.Wedge<Object, JobGraphLink>(10).transform(input);
                }
                return result;
            }
        };

    }

    protected boolean isCompoundRequirementLink(JobGraphLink link) {
        final ComponentRequirement req = link.getRequirement();
        final Object from = link.getFrom();
        if (req instanceof CompoundComponentRequirement && from instanceof HasFilterOutcomes) {
            if (((CompoundComponentRequirement) req).hasMultipleRequirementsFrom((HasFilterOutcomes) from)) {
                return true;
            }
        }
        return false;
    }

    private Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Shape> getBaseEdgeShapeTransformer(
            String edgeStyle) {
        if (edgeStyle == null) {
            return new EdgeShape.QuadCurve<>();
        }
        switch (edgeStyle) {
        case EDGE_STYLE_NAME_STRAIGHT:
            return new EdgeShape.Line<>();
        case EDGE_STYLE_NAME_CURVED:
            return new EdgeShape.QuadCurve<>();
        case EDGE_STYLE_NAME_ORTOGHONAL:
            return new EdgeShape.Orthogonal<>();
        }
        return new EdgeShape.QuadCurve<>();
    }

    public static final Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Number> EDGE_LABEL_CLOSENESS_TRANSFORMER = new Transformer<Context<Graph<Object, JobGraphLink>, JobGraphLink>, Number>() {
        @Override
        public Number transform(Context<Graph<Object, JobGraphLink>, JobGraphLink> input) {
            return 0.4d;
        }
    };

    public final EdgeLabelRenderer getEdgeLabelRenderer() {
        return new EdgeLabelRenderer() {
            @Override
            public void setRotateEdgeLabels(boolean state) {
            }

            @Override
            public boolean isRotateEdgeLabels() {
                return true;
            }

            @Override
            public <T> Component getEdgeLabelRendererComponent(JComponent vv, Object value, Font font,
                    boolean isSelected, T edge) {
                final String labelText;
                final String iconPath;

                if (edge instanceof JobGraphLink) {
                    final JobGraphLink link = (JobGraphLink) edge;
                    if (link.getOutputDataStream() != null) {
                        labelText = link.getLinkLabel();
                        iconPath = IconUtils.OUTPUT_DATA_STREAM_PATH;
                    } else if (isCompoundRequirementLink(link)) {
                        final HasFilterOutcomes from = (HasFilterOutcomes) link.getFrom();
                        final CompoundComponentRequirement req = (CompoundComponentRequirement) link
                                .getRequirement();
                        final Set<FilterOutcome> outcomesFrom = req.getOutcomesFrom(from);
                        labelText = new CompoundComponentRequirement(outcomesFrom).getSimpleName();
                        iconPath = IconUtils.FILTER_OUTCOME_PATH;
                    } else {
                        labelText = link.getLinkLabel();
                        iconPath = IconUtils.FILTER_OUTCOME_PATH;
                    }
                } else {
                    labelText = value + "";
                    iconPath = IconUtils.FILTER_OUTCOME_PATH;
                }

                final Icon icon = imageManager.getImageIcon(iconPath, IconUtils.ICON_SIZE_SMALL);
                final JLabel label = new JLabel(labelText, icon, JLabel.LEFT);
                label.setFont(_normalFont);
                return label;
            }
        };
    }

    public static final Transformer<Object, Icon> VERTEX_ICON_TRANSFORMER = new Transformer<Object, Icon>() {
        @Override
        public Icon transform(Object obj) {
            if (obj == JobGraph.MORE_COLUMNS_VERTEX || obj instanceof InputColumn) {
                return imageManager.getImageIcon(IconUtils.MODEL_COLUMN, IconUtils.ICON_SIZE_MEDIUM);
            }
            if (obj instanceof ComponentBuilder) {
                final ComponentBuilder componentBuilder = (ComponentBuilder) obj;
                final ComponentDescriptor<?> descriptor = componentBuilder.getDescriptor();
                final boolean configured;
                if (componentBuilder.getInput().length == 0) {
                    configured = true;
                } else {
                    configured = componentBuilder.isConfigured(false);
                }
                return IconUtils.getDescriptorIcon(descriptor, configured, IconUtils.ICON_SIZE_LARGE);
            }
            if (obj instanceof FilterOutcome) {
                return imageManager.getImageIcon(IconUtils.FILTER_OUTCOME_PATH, IconUtils.ICON_SIZE_MEDIUM);
            }
            if (obj instanceof Table) {
                return imageManager.getImageIcon(IconUtils.MODEL_TABLE, IconUtils.ICON_SIZE_LARGE);
            }
            if (obj instanceof Class) {
                Class<?> cls = (Class<?>) obj;
                if (ReflectionUtils.is(cls, AnalyzerResult.class)) {
                    return imageManager.getImageIcon(IconUtils.MODEL_RESULT, IconUtils.ICON_SIZE_LARGE);
                }
            }
            return imageManager.getImageIcon(IconUtils.STATUS_ERROR);
        }
    };

    public static final Transformer<Object, String> VERTEX_LABEL_TRANSFORMER = new Transformer<Object, String>() {
        @Override
        public String transform(Object obj) {
            if (obj instanceof InputColumn) {
                return ((InputColumn<?>) obj).getName();
            }
            if (obj instanceof ComponentBuilder) {
                return LabelUtils.getLabel((ComponentBuilder) obj);
            }
            if (obj instanceof FilterOutcome) {
                return ((FilterOutcome) obj).getCategory().name();
            }
            if (obj instanceof Table) {
                return ((Table) obj).getName();
            }
            if (obj instanceof Class) {
                Class<?> cls = (Class<?>) obj;
                if (ReflectionUtils.is(cls, AnalyzerResult.class)) {
                    return "Analyzer result";
                }
                return cls.getSimpleName();
            }
            return obj.toString();
        }
    };

    public static final Transformer<Object, Shape> VERTEX_SHAPE_TRANSFORMER = new Transformer<Object, Shape>() {
        @Override
        public Shape transform(Object input) {
            int size = IconUtils.ICON_SIZE_LARGE;
            int offset = -size / 2;
            return new Rectangle(new Point(offset, offset), new Dimension(size, size));
        }
    };

    public Transformer<Object, Font> getVertexFontTransformer() {
        return new Transformer<Object, Font>() {
            @Override
            public Font transform(Object vertex) {
                if (_highlighedVertexes.contains(vertex)) {
                    return _boldFont;
                }
                return _normalFont;
            }
        };
    }
}