All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.widgets.properties.PropertyWidgetFactoryImpl Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Free Software Foundation, Inc.
 *
 * 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.properties;

import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.inject.Inject;

import org.apache.metamodel.util.Resource;
import org.datacleaner.api.ColumnProperty;
import org.datacleaner.api.HiddenProperty;
import org.datacleaner.api.InputColumn;
import org.datacleaner.api.MappedProperty;
import org.datacleaner.api.SchemaProperty;
import org.datacleaner.api.TableProperty;
import org.datacleaner.bootstrap.WindowContext;
import org.datacleaner.connection.Datastore;
import org.datacleaner.connection.DatastoreCatalog;
import org.datacleaner.connection.UpdateableDatastore;
import org.datacleaner.descriptors.ComponentDescriptor;
import org.datacleaner.descriptors.ConfiguredPropertyDescriptor;
import org.datacleaner.guice.DCModule;
import org.datacleaner.job.builder.AnalyzerComponentBuilder;
import org.datacleaner.job.builder.ComponentBuilder;
import org.datacleaner.reference.Dictionary;
import org.datacleaner.reference.StringPattern;
import org.datacleaner.reference.SynonymCatalog;
import org.datacleaner.util.ReflectionUtils;

import com.google.inject.Injector;

/**
 * Represents a factory and a catalog of widgets used for @Configured
 * properties. A widget wanting to represent a component with @Configured
 * properties should keep it's reference to the property widget factory and use
 * it to retrieve the properties in case of listener callbacks.
 */
public final class PropertyWidgetFactoryImpl implements PropertyWidgetFactory {

    private final ComponentBuilder _componentBuilder;
    private final PropertyWidgetCollection _propertyWidgetCollection;
    private final DCModule _dcModule;

    @Inject
    protected PropertyWidgetFactoryImpl(final ComponentBuilder componentBuilder, final DCModule dcModule) {
        _componentBuilder = componentBuilder;
        _dcModule = dcModule;
        _propertyWidgetCollection = new PropertyWidgetCollection(componentBuilder);

        final Set mappedProperties =
                componentBuilder.getDescriptor().getConfiguredPropertiesByAnnotation(MappedProperty.class);
        for (final ConfiguredPropertyDescriptor mappedProperty : mappedProperties) {
            final MappedProperty annotation = mappedProperty.getAnnotation(MappedProperty.class);
            final String mappedToName = annotation.value();
            final ConfiguredPropertyDescriptor mappedToProperty =
                    componentBuilder.getDescriptor().getConfiguredProperty(mappedToName);

            final PropertyWidgetMapping propertyWidgetMapping =
                    buildMappedPropertyWidget(mappedProperty, mappedToProperty);

            _propertyWidgetCollection.putMappedPropertyWidget(mappedProperty, propertyWidgetMapping);
            _propertyWidgetCollection.putMappedPropertyWidget(mappedToProperty, propertyWidgetMapping);
        }
    }

    @Override
    public PropertyWidgetCollection getPropertyWidgetCollection() {
        return _propertyWidgetCollection;
    }

    protected PropertyWidgetMapping buildMappedPropertyWidget(final ConfiguredPropertyDescriptor mappedProperty,
            final ConfiguredPropertyDescriptor mappedToProperty) {
        if (mappedProperty.isArray() && mappedToProperty.isArray() && mappedToProperty.isInputColumn()) {
            // mapped strings
            if (mappedProperty.getBaseType() == String.class) {
                final MultipleMappedStringsPropertyWidget propertyWidget =
                        new MultipleMappedStringsPropertyWidget(getComponentBuilder(), mappedToProperty,
                                mappedProperty);
                final PropertyWidgetMapping mapping = new PropertyWidgetMapping();
                mapping.putMapping(mappedProperty, propertyWidget.getMappedStringsPropertyWidget());
                mapping.putMapping(mappedToProperty, propertyWidget);
                return mapping;
            }

            // mapped enums
            if (mappedProperty.getBaseType().isEnum()) {
                final MultipleMappedEnumsPropertyWidget propertyWidget =
                        new MultipleMappedEnumsPropertyWidget(getComponentBuilder(), mappedToProperty, mappedProperty);
                final PropertyWidgetMapping mapping = new PropertyWidgetMapping();
                mapping.putMapping(mappedProperty, propertyWidget.getMappedEnumsPropertyWidget());
                mapping.putMapping(mappedToProperty, propertyWidget);
                return mapping;
            }
        }

        // schema structure mapping
        if (mappedProperty.getBaseType() == String.class && !mappedToProperty.isArray()) {
            // save the "mappedToPropertyWidget" since it may be need to be
            // reused when there is a chain of dependencies between mapped
            // properties
            final PropertyWidget mappedToPropertyWidget =
                    _propertyWidgetCollection.getMappedPropertyWidget(mappedToProperty);

            // mapped schema name
            if (mappedProperty.getAnnotation(SchemaProperty.class) != null && (
                    mappedToProperty.getBaseType() == Datastore.class
                            || mappedToProperty.getBaseType() == UpdateableDatastore.class)) {
                final SchemaNamePropertyWidget schemaPropertyWidget =
                        new SchemaNamePropertyWidget(getComponentBuilder(), mappedProperty);
                final SingleDatastorePropertyWidget datastorePropertyWidget;
                if (mappedToPropertyWidget == null) {
                    final DatastoreCatalog datastoreCatalog =
                            getComponentBuilder().getAnalysisJobBuilder().getConfiguration().getDatastoreCatalog();
                    datastorePropertyWidget =
                            new SingleDatastorePropertyWidget(getComponentBuilder(), mappedToProperty, datastoreCatalog,
                                    _dcModule);
                } else {
                    datastorePropertyWidget = (SingleDatastorePropertyWidget) mappedToPropertyWidget;
                }

                datastorePropertyWidget.addComboListener(schemaPropertyWidget::setDatastore);

                final PropertyWidgetMapping mapping = new PropertyWidgetMapping();
                mapping.putMapping(mappedProperty, schemaPropertyWidget);
                mapping.putMapping(mappedToProperty, datastorePropertyWidget);
                return mapping;
            }

            // mapped table name
            if (mappedProperty.getAnnotation(TableProperty.class) != null
                    && mappedToProperty.getAnnotation(SchemaProperty.class) != null) {

                final SingleTableNamePropertyWidget tablePropertyWidget =
                        new SingleTableNamePropertyWidget(getComponentBuilder(), mappedProperty, getWindowContext());
                final SchemaNamePropertyWidget schemaPropertyWidget;
                if (mappedToPropertyWidget == null) {
                    schemaPropertyWidget = new SchemaNamePropertyWidget(getComponentBuilder(), mappedToProperty);
                } else {
                    schemaPropertyWidget = (SchemaNamePropertyWidget) mappedToPropertyWidget;
                }

                schemaPropertyWidget.connectToTableNamePropertyWidget(tablePropertyWidget);

                final PropertyWidgetMapping mapping = new PropertyWidgetMapping();
                mapping.putMapping(mappedProperty, tablePropertyWidget);
                mapping.putMapping(mappedToProperty, schemaPropertyWidget);
                return mapping;
            }

            // mapped column name(s)
            if (mappedProperty.getAnnotation(ColumnProperty.class) != null
                    && mappedToProperty.getAnnotation(TableProperty.class) != null) {

                final SingleTableNamePropertyWidget tablePropertyWidget;
                if (mappedToPropertyWidget == null) {
                    tablePropertyWidget = new SingleTableNamePropertyWidget(getComponentBuilder(), mappedToProperty,
                            getWindowContext());
                } else {
                    tablePropertyWidget = (SingleTableNamePropertyWidget) mappedToPropertyWidget;
                }

                if (mappedProperty.isArray()) {
                    // multiple mapped column names

                    // TODO: Not yet implemented. This case needs to take care
                    // of the fact that usually this is then ALSO mapped to an
                    // array of input columns.
                } else {
                    // mapped column name

                    final SingleColumnNamePropertyWidget columnPropertyWidget =
                            new SingleColumnNamePropertyWidget(mappedProperty, getComponentBuilder());
                    tablePropertyWidget.addComboListener(columnPropertyWidget::setTable);

                    final PropertyWidgetMapping mapping = new PropertyWidgetMapping();
                    mapping.putMapping(mappedProperty, columnPropertyWidget);
                    mapping.putMapping(mappedToProperty, tablePropertyWidget);
                    return mapping;
                }
            }
        }

        return null;
    }

    private WindowContext getWindowContext() {
        return _dcModule.createChildInjectorForComponent(_componentBuilder).getInstance(WindowContext.class);
    }

    @Override
    public ComponentBuilder getComponentBuilder() {
        return _componentBuilder;
    }

    protected Injector getInjectorForPropertyWidgets(final ConfiguredPropertyDescriptor propertyDescriptor) {
        return _dcModule.createChildInjectorForProperty(_componentBuilder, propertyDescriptor);
    }

    @Override
    public PropertyWidget create(final String propertyName) {
        final ComponentDescriptor descriptor = _componentBuilder.getDescriptor();
        final ConfiguredPropertyDescriptor propertyDescriptor = descriptor.getConfiguredProperty(propertyName);
        if (propertyDescriptor == null) {
            throw new IllegalArgumentException("No such property: " + propertyName);
        }
        return create(propertyDescriptor);
    }

    /**
     * Creates (and registers) a widget that fits the specified configured
     * property.
     *
     * @param propertyDescriptor
     * @return
     */
    @Override
    public PropertyWidget create(final ConfiguredPropertyDescriptor propertyDescriptor) {
        // first check if there is a mapping created for this property
        // descriptor
        final PropertyWidget propertyWidget = _propertyWidgetCollection.getMappedPropertyWidget(propertyDescriptor);
        if (propertyWidget != null) {
            return propertyWidget;
        }
        final HiddenProperty hiddenProperty = propertyDescriptor.getAnnotation(HiddenProperty.class);
        if (hiddenProperty != null && hiddenProperty.hiddenForLocalAccess()) {
            return null;
        }
        if (propertyDescriptor.getAnnotation(Deprecated.class) != null) {
            return null;
        }

        if (getComponentBuilder() instanceof AnalyzerComponentBuilder) {
            final AnalyzerComponentBuilder analyzer = (AnalyzerComponentBuilder) getComponentBuilder();
            if (analyzer.isMultipleJobsSupported()) {
                if (analyzer.isMultipleJobsDeterminedBy(propertyDescriptor)) {
                    return new MultipleInputColumnsPropertyWidget(analyzer, propertyDescriptor);
                }
            }
        }

        // check for fitting property widgets by type
        final Class type = propertyDescriptor.getBaseType();

        final Class> widgetClass;
        if (propertyDescriptor.isArray()) {
            if (propertyDescriptor.isInputColumn()) {
                widgetClass = MultipleInputColumnsPropertyWidget.class;
            } else if (ReflectionUtils.isString(type)) {
                widgetClass = MultipleStringPropertyWidget.class;
            } else if (type == Dictionary.class) {
                widgetClass = MultipleDictionariesPropertyWidget.class;
            } else if (type == SynonymCatalog.class) {
                widgetClass = MultipleSynonymCatalogsPropertyWidget.class;
            } else if (type == StringPattern.class) {
                widgetClass = MultipleStringPatternPropertyWidget.class;
            } else if (type.isEnum()) {
                widgetClass = MultipleEnumPropertyWidget.class;
            } else if (type == Class.class) {
                widgetClass = MultipleClassesPropertyWidget.class;
            } else if (type == char.class) {
                widgetClass = MultipleCharPropertyWidget.class;
            } else if (ReflectionUtils.isNumber(type)) {
                widgetClass = MultipleNumberPropertyWidget.class;
            } else {
                // not yet implemented
                widgetClass = DummyPropertyWidget.class;
            }
        } else {

            if (propertyDescriptor.isInputColumn()) {
                if (_componentBuilder.getDescriptor().getConfiguredPropertiesByType(InputColumn.class, true).size()
                        == 1) {
                    // if there is only a single input column property, it
                    // will
                    // be displayed using radiobuttons.
                    widgetClass = SingleInputColumnRadioButtonPropertyWidget.class;
                } else {
                    // if there are multiple input column properties, they
                    // will
                    // be displayed using combo boxes.
                    widgetClass = SingleInputColumnComboBoxPropertyWidget.class;
                }
            } else if (ReflectionUtils.isCharacter(type)) {
                widgetClass = SingleCharacterPropertyWidget.class;
            } else if (ReflectionUtils.isString(type)) {
                widgetClass = SingleStringPropertyWidget.class;
            } else if (ReflectionUtils.isBoolean(type)) {
                widgetClass = SingleBooleanPropertyWidget.class;
            } else if (ReflectionUtils.isNumber(type)) {
                widgetClass = SingleNumberPropertyWidget.class;
            } else if (ReflectionUtils.isDate(type)) {
                widgetClass = SingleDatePropertyWidget.class;
            } else if (type == Dictionary.class) {
                widgetClass = SingleDictionaryPropertyWidget.class;
            } else if (type == SynonymCatalog.class) {
                widgetClass = SingleSynonymCatalogPropertyWidget.class;
            } else if (type == StringPattern.class) {
                widgetClass = SingleStringPatternPropertyWidget.class;
            } else if (type.isEnum()) {
                widgetClass = SingleEnumPropertyWidget.class;
            } else if (ReflectionUtils.is(type, Resource.class)) {
                widgetClass = SingleResourcePropertyWidget.class;
            } else if (type == File.class) {
                widgetClass = SingleFilePropertyWidget.class;
            } else if (type == Pattern.class) {
                widgetClass = SinglePatternPropertyWidget.class;
            } else if (ReflectionUtils.is(type, Datastore.class)) {
                widgetClass = SingleDatastorePropertyWidget.class;
            } else if (type == Class.class) {
                widgetClass = SingleClassPropertyWidget.class;
            } else if (type == Map.class) {
                final Class genericType1 = propertyDescriptor.getTypeArgument(0);
                final Class genericType2 = propertyDescriptor.getTypeArgument(1);
                if (genericType1 == String.class && genericType2 == String.class) {
                    widgetClass = MapStringToStringPropertyWidget.class;
                } else {
                    // not yet implemented
                    widgetClass = DummyPropertyWidget.class;
                }
            } else {
                // not yet implemented
                widgetClass = DummyPropertyWidget.class;
            }
        }

        final Injector injector = getInjectorForPropertyWidgets(propertyDescriptor);
        return injector.getInstance(widgetClass);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy