org.deegree.igeo.views.swing.style.component.classification.AbstractClassificationPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.deegree.igeo.views.swing.style.component.classification.AbstractClassificationPanel.java

Source

/*----------------    FILE HEADER  ------------------------------------------
 This file is part of deegree.
 Copyright (C) 2001-2012 by:
 Department of Geography, University of Bonn
 http://www.giub.uni-bonn.de/deegree/
 lat/lon GmbH
 http://www.lat-lon.de
    
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 This library 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 library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 Contact:
    
 lat/lon GmbH
 Aennchenstr. 19
 53177 Bonn
 Germany
 E-Mail: info@lat-lon.de
    
 Prof. Dr. Klaus Greve
 Department of Geography
 University of Bonn
 Meckenheimer Allee 166
 53115 Bonn
 Germany
 E-Mail: greve@giub.uni-bonn.de
 ---------------------------------------------------------------------------*/

package org.deegree.igeo.views.swing.style.component.classification;

import static org.deegree.igeo.i18n.Messages.get;
import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FILLCOLOR;
import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.LINESTYLE;
import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.VALUE;
import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.EQUAL;
import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.QUALITY;
import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.QUANTILE;
import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.UNIQUE;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import org.deegree.datatypes.QualifiedName;
import org.deegree.datatypes.Types;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.graphics.sld.Rule;
import org.deegree.igeo.style.LayerCache.CachedLayer;
import org.deegree.igeo.style.classification.ClassificationFromSld;
import org.deegree.igeo.style.model.DashArray;
import org.deegree.igeo.style.model.GraphicSymbol;
import org.deegree.igeo.style.model.Histogram;
import org.deegree.igeo.style.model.PropertyValue;
import org.deegree.igeo.style.model.RandomColors;
import org.deegree.igeo.style.model.SldProperty;
import org.deegree.igeo.style.model.SldValues;
import org.deegree.igeo.style.model.WellKnownMark;
import org.deegree.igeo.style.model.classification.AbstractClassification;
import org.deegree.igeo.style.model.classification.ClassificationTableRow;
import org.deegree.igeo.style.model.classification.Column;
import org.deegree.igeo.style.model.classification.EqualIntervalClassification;
import org.deegree.igeo.style.model.classification.IllegalClassificationException;
import org.deegree.igeo.style.model.classification.Intervallable;
import org.deegree.igeo.style.model.classification.Intervallables.DateIntervallable;
import org.deegree.igeo.style.model.classification.Intervallables.DoubleIntervallable;
import org.deegree.igeo.style.model.classification.Intervallables.StringIntervallable;
import org.deegree.igeo.style.model.classification.ManualClassification;
import org.deegree.igeo.style.model.classification.QualityClassification;
import org.deegree.igeo.style.model.classification.QuantileClassification;
import org.deegree.igeo.style.model.classification.ThematicGrouping;
import org.deegree.igeo.style.model.classification.ThematicGroupingInformation;
import org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE;
import org.deegree.igeo.style.model.classification.UniqueValueGrouping;
import org.deegree.igeo.style.model.classification.ValueRange;
import org.deegree.igeo.views.swing.addlayer.QualifiedNameRenderer;
import org.deegree.igeo.views.swing.style.SingleItem;
import org.deegree.igeo.views.swing.style.SingleItemDisableComboBox;
import org.deegree.igeo.views.swing.style.StyleDialog;
import org.deegree.igeo.views.swing.style.StyleDialogUtils;
import org.deegree.igeo.views.swing.style.VisualPropertyPanel;
import org.deegree.igeo.views.swing.style.component.font.FontHelper;
import org.deegree.igeo.views.swing.style.editor.ClassificationValuesEditor;
import org.deegree.igeo.views.swing.style.editor.ColorTableCellEditor;
import org.deegree.igeo.views.swing.style.editor.FillTableCellEditor;
import org.deegree.igeo.views.swing.style.editor.PointCellEditor;
import org.deegree.igeo.views.swing.style.editor.SpinnerTableCellEditor;
import org.deegree.igeo.views.swing.style.renderer.ClassificationValuesRenderer;
import org.deegree.igeo.views.swing.style.renderer.ColorTableCellRenderer;
import org.deegree.igeo.views.swing.style.renderer.DashArrayRenderer;
import org.deegree.igeo.views.swing.style.renderer.LocaleTableCellRenderer;
import org.deegree.igeo.views.swing.style.renderer.SldPropertyCellRenderer;
import org.deegree.igeo.views.swing.style.renderer.SldPropertyRenderer;
import org.deegree.igeo.views.swing.style.renderer.SymbolRenderer;
import org.deegree.igeo.views.swing.style.renderer.SymbolTableCellRenderer;
import org.deegree.igeo.views.swing.util.IconRegistry;
import org.deegree.model.feature.schema.FeatureType;
import org.deegree.model.filterencoding.FilterEvaluationException;
import org.deegree.model.filterencoding.PropertyName;
import org.deegree.ogcbase.PropertyPath;
import org.deegree.ogcbase.PropertyPathFactory;

import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * <code>AbstractClassificationPanel</code> TODO add class documentation
 * 
 * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a>
 * @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a>
 * @author last edited by: $Author$
 * 
 * @version $Revision$, $Date$
 * 
 */
public abstract class AbstractClassificationPanel extends JPanel implements ActionListener, TableModelListener {

    private static final long serialVersionUID = -7782382934136012149L;

    private static final ILogger LOG = LoggerFactory.getLogger(AbstractClassificationPanel.class);

    private SingleItem unknownClassification;

    private SingleItem uniqueValue;

    private SingleItem equalInterval;

    private SingleItem quantile;

    private SingleItem quality;

    public enum SYMBOLIZERTYPE {
        POLYGON, POINT, LINE, LABEL
    }

    protected VisualPropertyPanel assignedVisualPropPanel;

    protected JComboBox propertyCB;

    private ClassificationTableModel<?> model;

    private JCheckBox status;

    private SingleItemDisableComboBox classificationTypeCB;

    private JLabel isManual = new JLabel(get("$MD10735"));

    private JSpinner numberOfClassesSpinner;

    private JTable classesTable;

    private boolean isUpdating = false;

    private boolean isInitialising = true;

    private JButton addRowBt;

    private JButton removeRowBt;

    private JButton openHistogramBt;

    private String datePattern;

    private String decimalPattern;

    private Histogram histogram;

    private JRadioButton fullDatabase;

    private JRadioButton extentDatabase;

    /**
     * 
     * @param assignedVisualPropPanel
     */
    public AbstractClassificationPanel(VisualPropertyPanel assignedVisualPropPanel) {
        this.assignedVisualPropPanel = assignedVisualPropPanel;
        datePattern = assignedVisualPropPanel.getOwner().getSettings().getFormatsOptions()
                .getPattern("datePattern");
        decimalPattern = assignedVisualPropPanel.getOwner().getSettings().getFormatsOptions()
                .getPattern("decimalPattern");
        setLayout(new BorderLayout());
        init();
    }

    /**
     * @return true, if classification is enabled; false otherwise
     */
    public boolean isActive() {
        return status.isSelected();
    }

    /**
     * @return the list of rules, representing the classification
     */
    public List<Rule> getRules() {
        PropertyName propertyName = new PropertyName((QualifiedName) propertyCB.getSelectedItem());
        return model.getClassifiedData(getSymbolizerType(), propertyName);
    }

    /**
     * Constructs a manual classification out of the given rules. Only the first symbolizer of each rule will be
     * considered, if it is a PolygonSymbolizer!.
     * 
     * @param the
     *            rules to construct a classification
     * @param type
     *            the type of the featuretypeName
     * @throws FilterEvaluationException
     */
    public void setValues(List<Rule> rules, FeatureType featureType)
            throws IllegalClassificationException, FilterEvaluationException {

        PropertyName propertyName = ClassificationFromSld.detectPropertyName(rules);
        if (propertyName == null) {
            throw new IllegalClassificationException("PopertyNames are not valid!");
        }
        // TODO: check if this is necessary!?
        boolean isTypeCorrect = ClassificationFromSld.isTypeCorrect(rules, getSymbolizerType());
        if (!isTypeCorrect) {
            throw new IllegalClassificationException(
                    "Could not create list of value ranges - symbolizer is not of type " + getSymbolizerType());
        }

        int propertyType = ClassificationFromSld.detectPropertyType(propertyName, featureType);

        switch (propertyType) {
        case Types.INTEGER:
        case Types.SMALLINT:
        case Types.BIGINT:
        case Types.DOUBLE:
        case Types.FLOAT:
            setPropertyCB(propertyName);
            PropertyValue<?> pvDouble = getPropertyValue();
            List<Intervallable<Double>> doubleData = getDoubleData(pvDouble);
            ThematicGroupingInformation<Double> tgiDouble = ClassificationFromSld.createDoubleClassification(rules,
                    doubleData, propertyType, decimalPattern, assignedVisualPropPanel.getOwner().getSettings());
            ThematicGrouping<Double> groupingDouble;
            groupingDouble = getGrouping(tgiDouble);
            numberOfClassesSpinner.setValue(tgiDouble.getRows().size());

            groupingDouble.setData(doubleData);
            ClassificationTableModel<Double> tableModelDouble = new ClassificationTableModel<Double>(getColumns(),
                    assignedVisualPropPanel.getOwner());
            tableModelDouble.setClassification(tgiDouble.getRows(), tgiDouble.getType());
            tableModelDouble.setThematicGrouping(groupingDouble, tgiDouble.getType());
            configureClassesTable(tableModelDouble, groupingDouble.getAttributeHeader(),
                    new ClassificationValuesRenderer<Double>(),
                    new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern)));
            break;
        case Types.DATE:
            setPropertyCB(propertyName);
            PropertyValue<?> pvDate = getPropertyValue();
            List<Intervallable<Date>> dateData = getDateData(pvDate);
            ThematicGroupingInformation<Date> tgiDate = ClassificationFromSld.createDateClassification(rules,
                    dateData, propertyType, datePattern, assignedVisualPropPanel.getOwner().getSettings());
            ThematicGrouping<Date> groupingDate;
            groupingDate = getGrouping(tgiDate);
            numberOfClassesSpinner.setValue(tgiDate.getRows().size());

            groupingDate.setData(dateData);
            ClassificationTableModel<Date> tableModelDate = new ClassificationTableModel<Date>(getColumns(),
                    assignedVisualPropPanel.getOwner());
            tableModelDate.setClassification(tgiDate.getRows(), tgiDate.getType());
            tableModelDate.setThematicGrouping(groupingDate, tgiDate.getType());
            configureClassesTable(tableModelDate, groupingDate.getAttributeHeader(),
                    new ClassificationValuesRenderer<Date>(),
                    new ClassificationValuesEditor<Date>(new DateIntervallable(new Date(), datePattern)));
            break;
        case Types.VARCHAR:
            setPropertyCB(propertyName);
            PropertyValue<?> pvString = getPropertyValue();
            List<Intervallable<String>> stringData = getStringData(pvString);
            ThematicGroupingInformation<String> tgiString = ClassificationFromSld.createStringClassification(rules,
                    stringData, propertyType, assignedVisualPropPanel.getOwner().getSettings());
            ThematicGrouping<String> groupingString;
            switch (tgiString.getType()) {
            case UNIQUE:
                groupingString = new UniqueValueGrouping<String>();
                classificationTypeCB.setSelectedItem(uniqueValue);
                numberOfClassesSpinner.setEnabled(false);
                break;
            case QUALITY:
                groupingString = new QualityClassification<String>();
                classificationTypeCB.setSelectedItem(quality);
                numberOfClassesSpinner.setEnabled(false);
                break;
            default:
                groupingString = new ManualClassification<String>();
                numberOfClassesSpinner.setValue(tgiString.getRows().size());
                unknownClassification.setEnabled(true);
                classificationTypeCB.setSelectedItem(unknownClassification);
                break;
            }
            numberOfClassesSpinner.setValue(tgiString.getRows().size());
            numberOfClassesSpinner.setEnabled(false);

            groupingString.setData(stringData);
            ClassificationTableModel<String> tableModelString = new ClassificationTableModel<String>(getColumns(),
                    assignedVisualPropPanel.getOwner());
            tableModelString.setClassification(tgiString.getRows(), tgiString.getType());
            tableModelString.setThematicGrouping(groupingString, tgiString.getType());
            configureClassesTable(tableModelString, groupingString.getAttributeHeader(),
                    new ClassificationValuesRenderer<String>(),
                    new ClassificationValuesEditor<String>(new StringIntervallable("dummy")));
            break;
        default:
            LOG.logInfo("not supported type:  " + propertyType);
            break;
        }

        status.setSelected(true);
    }

    private PropertyValue<?> getPropertyValue() {
        StyleDialog styleDialog = assignedVisualPropPanel.getOwner();
        if (fullDatabase.isSelected()) {
            return styleDialog.getAllPropertyValue((QualifiedName) this.propertyCB.getSelectedItem());
        } else if (extentDatabase.isSelected()) {
            return styleDialog.getExtentPropertyValue((QualifiedName) this.propertyCB.getSelectedItem());
        }
        return styleDialog.getPropertyValue((QualifiedName) this.propertyCB.getSelectedItem());
    }

    private <V extends Comparable<V>> ThematicGrouping<V> getGrouping(
            ThematicGroupingInformation<V> thematicGrouping) {
        GROUPINGTYPE type = thematicGrouping.getType();
        // TODO: ask user for classification
        //        if ( !thematicGrouping.getType().equals( UNIQUE ) && !thematicGrouping.getType().equals( QUANTILE )
        //             && !thematicGrouping.getType().equals( EQUAL ) && !thematicGrouping.getType().equals( QUALITY ) ) {
        //             type = askUserForClassificationType();
        //        }
        ThematicGrouping<V> grouping;
        switch (type) {
        case UNIQUE:
            grouping = new UniqueValueGrouping<V>();
            classificationTypeCB.setSelectedItem(uniqueValue);
            numberOfClassesSpinner.setEnabled(false);
            break;
        case QUANTILE:
            grouping = new QuantileClassification<V>();
            classificationTypeCB.setSelectedItem(quantile);
            break;
        case EQUAL:
            grouping = new EqualIntervalClassification<V>();
            classificationTypeCB.setSelectedItem(equalInterval);
            break;
        case QUALITY:
            grouping = new QualityClassification<V>();
            classificationTypeCB.setSelectedItem(quality);
            break;
        default:
            grouping = new ManualClassification<V>();
            unknownClassification.setEnabled(true);
            classificationTypeCB.setSelectedItem(unknownClassification);
            break;
        }
        return grouping;
    }

    protected ClassificationTableModel<?> getModel() {
        if (model == null) {
            updateTable();
        }
        return model;
    }

    private void init() {
        status = new JCheckBox();
        status.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (isActive()) {
                    assignedVisualPropPanel.setActive(true);
                }
            }
        });
        classesTable = new ClassesTable();
        // nr of properties which can be classified

        List<QualifiedName> pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE,
                Types.INTEGER, Types.SMALLINT, Types.BIGINT, Types.DATE);
        List<QualifiedName> pvListString = assignedVisualPropPanel.getOwner().getPropertyNames(Types.VARCHAR);

        boolean isStringClassEnabled = true;
        boolean isClassEnabled = true;
        if (pvList.size() == 0) {
            isClassEnabled = false;
            if (pvListString.size() == 0) {
                isStringClassEnabled = false;
            }
        }

        uniqueValue = new SingleItem(get("$MD10724"), true);
        equalInterval = new SingleItem(get("$MD10725"), isClassEnabled);
        quantile = new SingleItem(get("$MD10726"), isClassEnabled);
        quality = new SingleItem(get("$MD11531"), isStringClassEnabled);
        unknownClassification = new SingleItem(get("$MD11849"), false);

        List<SingleItem> classificationItems = new ArrayList<SingleItem>(4);
        classificationItems.add(uniqueValue);
        classificationItems.add(equalInterval);
        classificationItems.add(quantile);
        classificationItems.add(quality);
        classificationItems.add(unknownClassification);

        classificationTypeCB = new SingleItemDisableComboBox(classificationItems);

        classificationTypeCB.addActionListener(this);

        propertyCB = new JComboBox();
        QualifiedNameRenderer renderer = new QualifiedNameRenderer();
        renderer.setPreferredSize(new Dimension(100, 18));
        propertyCB.setRenderer(renderer);
        propertyCB.addActionListener(this);

        SpinnerModel noClassesModel = new SpinnerNumberModel(AbstractClassification.DEFAULTNOOFCLASSES, 1,
                Integer.MAX_VALUE, 1);
        numberOfClassesSpinner = new JSpinner(noClassesModel);
        numberOfClassesSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                if (!isManual.isVisible()) {
                    model.getThematicGrouping().setNoOfClasses((Integer) numberOfClassesSpinner.getValue());
                    model.update(VALUE, true);
                }
                setClassificationActive();
            }
        });

        openHistogramBt = new JButton(get("$MD11049"));
        openHistogramBt.addActionListener(this);
        openHistogramBt.setVisible(true);
        histogram = new Histogram();
        histogram.addWindowListener(new WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent e) {
                openHistogramBt.setText(get("$MD11049"));
            };
        });

        initDatabaseButtonGroup();

        classificationTypeCB.setSelectedItem(uniqueValue);
        isManual.setVisible(false);

        addRowBt = new JButton(get("$MD10739"), IconRegistry.getIcon("textfield_add.png"));
        addRowBt.addActionListener(this);
        removeRowBt = new JButton(get("$MD10740"), IconRegistry.getIcon("textfield_delete.png"));
        removeRowBt.setEnabled(false);
        removeRowBt.addActionListener(this);

        // set selection mode
        classesTable.setRowSelectionAllowed(true);
        classesTable.setColumnSelectionAllowed(false);
        classesTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        classesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (classesTable.getSelectedRows().length > 0) {
                    removeRowBt.setEnabled(true);
                } else {
                    removeRowBt.setEnabled(false);
                }
            }
        });

        // mouseListener
        classesTable.addMouseListener(new TableMouseListener(classesTable));

        // set column model (width of the columns and editors)
        TableColumnModel columnModel = classesTable.getColumnModel();
        for (int i = 0; i < classesTable.getColumnCount(); i++) {
            TableColumn column = columnModel.getColumn(i);
            switch (model.getColumnType(i)) {
            case VALUE:
                column.setPreferredWidth(100);
                break;
            case FILLCOLOR:
            case LINECOLOR:
            case FILLTRANSPARENCY:
            case LINETRANSPARENCY:
            case FONTTRANSPARENCY:
            case LINEWIDTH:
            case LINESTYLE:
            case SIZE:
            case LINECAP:
            case FONTFAMILY:
            case HALOCOLOR:
            case HALORADIUS:
            case FONTCOLOR:
            case FONTSIZE:
            case FONTSTYLE:
            case FONTWEIGHT:
            case ROTATION:
                column.setPreferredWidth(75);
                break;
            case SYMBOL:
                column.setPreferredWidth(100);
                break;
            }
        }
        updateTable();

        int nrOfClasses = 6;
        if (model != null) {
            nrOfClasses = model.getRows().size();
        }
        numberOfClassesSpinner.setValue(nrOfClasses);
        // numberOfClassesSpinner.setValue( (Integer) model.getRows().size() );

        JTableHeader header = classesTable.getTableHeader();
        header.addMouseListener(new TableHeaderMouseListener(classesTable, assignedVisualPropPanel));
        header.setReorderingAllowed(false);

        JScrollPane scrollPane = new JScrollPane(classesTable);
        classesTable.setPreferredScrollableViewportSize(new Dimension(150, 190));

        // layout
        FormLayout fl = new FormLayout(
                "10dlu, left:min:grow(0.2), min:grow(0.3), $rgap, min:grow(0.3), $rgap, min:grow(0.2)",
                "bottom:15dlu, $cpheight, $cpheight, $cpheight, default:grow(1.0), $cpheight");
        DefaultFormBuilder builder = new DefaultFormBuilder(fl);

        builder.setBorder(StyleDialogUtils.createStyleAttributeBorder(get("$MD10719")));

        CellConstraints cc = new CellConstraints();
        builder.add(status, cc.xy(1, 1));
        builder.addLabel(get("$MD10720"), cc.xy(2, 1));

        builder.addLabel(get("$MD10721"), cc.xy(2, 2));
        builder.add(classificationTypeCB, cc.xy(3, 2));
        builder.add(isManual, cc.xy(5, 2));
        builder.addLabel(get("$MD10722"), cc.xy(2, 3));
        builder.add(propertyCB, cc.xywh(3, 3, 3, 1));
        builder.add(openHistogramBt, cc.xy(5, 4));
        builder.addLabel(get("$MD10723"), cc.xy(2, 4));
        builder.add(numberOfClassesSpinner, cc.xy(3, 4));

        builder.add(getDatabasePanel(), cc.xywh(7, 2, 1, 3));
        builder.add(scrollPane, cc.xyw(1, 5, 7));
        builder.add(buildTableChangerButtonBar(), cc.xyw(1, 6, 5, CellConstraints.CENTER, CellConstraints.BOTTOM));

        add(builder.getPanel(), BorderLayout.CENTER);
        isInitialising = false;
    }

    private void initDatabaseButtonGroup() {
        fullDatabase = new JRadioButton(get("$MD11867"));
        fullDatabase.addActionListener(this);
        extentDatabase = new JRadioButton(get("$MD11868"));
        extentDatabase.addActionListener(this);
        ButtonGroup bgDatabase = new ButtonGroup();
        bgDatabase.add(fullDatabase);
        bgDatabase.add(extentDatabase);
        extentDatabase.setSelected(true);
    }

    private JPanel getDatabasePanel() {
        FormLayout fl = new FormLayout("10dlu, left:default", "default, default, default");
        DefaultFormBuilder builder = new DefaultFormBuilder(fl);
        CellConstraints cc = new CellConstraints();
        builder.addLabel(get("$MD11866"), cc.xyw(1, 1, 2));
        builder.add(extentDatabase, cc.xy(2, 2));
        builder.add(fullDatabase, cc.xy(2, 3));
        JPanel panel = builder.getPanel();
        CachedLayer cachedLayer = assignedVisualPropPanel.getOwner().getCachedLayer();
        if (cachedLayer.isFullLoadingSupported()) {
            panel.setVisible(true);
        } else {
            panel.setVisible(false);
        }
        return panel;
    }

    private void setPropertyCB(PropertyName propertyName) throws IllegalClassificationException {
        boolean isPropertyNameValid = false;
        // init Property ComboBox
        for (int i = 0; i < propertyCB.getItemCount(); i++) {
            PropertyPath itemAsPath = PropertyPathFactory
                    .createPropertyPath((QualifiedName) propertyCB.getItemAt(i));
            if (ClassificationFromSld.equalsPropertyNameWithotNS(itemAsPath, propertyName.getValue())) {
                propertyCB.setSelectedIndex(i);
                isPropertyNameValid = true;
                break;
            }
        }

        if (!isPropertyNameValid) {
            throw new IllegalClassificationException(
                    "property name used in the classification is not available for classification");
        }
    }

    private void configureClassesTable(ClassificationTableModel<?> model, String valueHeader,
            ClassificationValuesRenderer<?> renderer, ClassificationValuesEditor<?> editor) {
        classesTable.setModel(model);
        int valueIndex = model.getColumnIndex(VALUE);
        classesTable.getColumnModel().getColumn(valueIndex).setHeaderValue(valueHeader);
        classesTable.getColumnModel().getColumn(valueIndex).setCellRenderer(renderer);
        classesTable.getColumnModel().getColumn(valueIndex).setCellEditor(editor);
        model.addTableModelListener(this);
        this.model = model;
        editor.addCellEditorListener(model);
    }

    // returns a list of double intervallables for TYPES.INTEGER and TYPES.DOUBLE types
    private List<Intervallable<Double>> getDoubleData(PropertyValue<?> pv) {
        List<Intervallable<Double>> data = new ArrayList<Intervallable<Double>>();
        for (Object o : pv.getValues()) {
            double doubleValue = Double.NaN;
            try {
                if (o != null && o instanceof String && ((String) o).length() > 0) {
                    doubleValue = Double.parseDouble((String) o);
                } else if (o instanceof Number) {
                    doubleValue = ((Number) o).doubleValue();
                }
            } catch (Exception e) {
                LOG.logError("Could not cast value to double, where type is INTEGER or DOUBLE");
            }
            data.add(new DoubleIntervallable(doubleValue, decimalPattern));
        }
        return data;
    }

    private List<Intervallable<Date>> getDateData(PropertyValue<?> pv) {
        List<Intervallable<Date>> data = new ArrayList<Intervallable<Date>>();
        for (Object o : pv.getValues()) {
            data.add(new DateIntervallable((Date) o, datePattern));
        }
        return data;
    }

    private List<Intervallable<String>> getStringData(PropertyValue<?> pv) {
        List<Intervallable<String>> data = new ArrayList<Intervallable<String>>();
        for (Object o : pv.getValues()) {
            String stringValue = "";
            try {
                if (o != null && ((String) o).length() > 0) {
                    stringValue = (String) o;
                }
            } catch (Exception e) {
                LOG.logError("Could not cast value to string, where type is VARCHAR");
            }
            data.add(new StringIntervallable(stringValue));
        }
        return data;
    }

    private void updateTable() {
        if (this.propertyCB.getSelectedItem() != null) {
            PropertyValue<?> pv = getPropertyValue();

            switch (pv.getDatatyp()) {
            case Types.INTEGER:
            case Types.SMALLINT:
            case Types.BIGINT:
                List<Intervallable<Double>> dataInteger = new ArrayList<Intervallable<Double>>();
                for (Object o : pv.getValues()) {
                    if (pv.getDatatyp() == Types.BIGINT) {
                        dataInteger.add(new DoubleIntervallable(((BigInteger) o).doubleValue(), decimalPattern));
                    } else {
                        dataInteger.add(new DoubleIntervallable((double) ((Integer) o), decimalPattern));
                    }
                }
                ClassificationTableModel<Double> tableModelInteger = new ClassificationTableModel<Double>(
                        getColumns(), assignedVisualPropPanel.getOwner());
                ThematicGrouping<Double> groupingInteger = getGrouping(dataInteger,
                        (Integer) numberOfClassesSpinner.getValue());
                tableModelInteger.setThematicGrouping(groupingInteger, getGroupingType());
                configureClassesTable(tableModelInteger, groupingInteger.getAttributeHeader(),
                        new ClassificationValuesRenderer<Double>(),
                        new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern)));
                break;
            case Types.DOUBLE:
            case Types.FLOAT:
                List<Intervallable<Double>> dataDouble = new ArrayList<Intervallable<Double>>();
                for (Object o : pv.getValues()) {
                    dataDouble.add(new DoubleIntervallable((Double) o, decimalPattern));
                }

                ClassificationTableModel<Double> tableModelDouble = new ClassificationTableModel<Double>(
                        getColumns(), assignedVisualPropPanel.getOwner());
                ThematicGrouping<Double> groupingDouble = getGrouping(dataDouble,
                        (Integer) numberOfClassesSpinner.getValue());
                tableModelDouble.setThematicGrouping(groupingDouble, getGroupingType());
                configureClassesTable(tableModelDouble, groupingDouble.getAttributeHeader(),
                        new ClassificationValuesRenderer<Double>(),
                        new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern)));
                break;
            case Types.DATE:
                ClassificationTableModel<Date> tableModelDate = new ClassificationTableModel<Date>(getColumns(),
                        assignedVisualPropPanel.getOwner());
                List<Intervallable<Date>> dataDate = new ArrayList<Intervallable<Date>>();
                for (Object o : pv.getValues()) {
                    dataDate.add(new DateIntervallable((Date) o, datePattern));
                }
                ThematicGrouping<Date> groupingDate = getGrouping(dataDate,
                        (Integer) numberOfClassesSpinner.getValue());
                tableModelDate.setThematicGrouping(groupingDate, getGroupingType());
                configureClassesTable(tableModelDate, groupingDate.getAttributeHeader(),
                        new ClassificationValuesRenderer<Date>(),
                        new ClassificationValuesEditor<Date>(new DateIntervallable(new Date(), datePattern)));
                break;
            case Types.VARCHAR:
            default:
                ClassificationTableModel<String> tableModelString = new ClassificationTableModel<String>(
                        getColumns(), assignedVisualPropPanel.getOwner());
                List<Intervallable<String>> dataString = new ArrayList<Intervallable<String>>();
                for (Object o : pv.getValues()) {
                    dataString.add(new StringIntervallable((String) o));
                }
                ThematicGrouping<String> groupingString = getGrouping(dataString,
                        (Integer) numberOfClassesSpinner.getValue());

                tableModelString.setThematicGrouping(groupingString, getGroupingType());
                configureClassesTable(tableModelString, groupingString.getAttributeHeader(),
                        new ClassificationValuesRenderer<String>(),
                        new ClassificationValuesEditor<String>(new StringIntervallable("dummy")));
                break;
            }
            if (classificationTypeCB.getSelectedItem() == uniqueValue
                    || classificationTypeCB.getSelectedItem() == quality) {
                getModel().getThematicGrouping().setFillColor(new RandomColors());
                model.update(FILLCOLOR, true);
            }
            model.update(VALUE, true);
        }

    }

    private GROUPINGTYPE getGroupingType() {
        if (classificationTypeCB.getSelectedItem() == equalInterval) {
            return EQUAL;
        } else if (classificationTypeCB.getSelectedItem() == quantile) {
            return QUANTILE;
        } else if (classificationTypeCB.getSelectedItem() == quality) {
            return QUALITY;
        } else if (classificationTypeCB.getSelectedItem() == uniqueValue) {
            return UNIQUE;
        }
        return GROUPINGTYPE.MANUAL;
    }

    private <V extends Comparable<V>> ThematicGrouping<V> getGrouping(List<Intervallable<V>> data,
            int noOfClasses) {
        ThematicGrouping<V> grouping;
        if (classificationTypeCB.getSelectedItem() == equalInterval) {
            grouping = new EqualIntervalClassification<V>();
        } else if (classificationTypeCB.getSelectedItem() == quantile) {
            grouping = new QuantileClassification<V>();
        } else if (classificationTypeCB.getSelectedItem() == quality) {
            grouping = new QualityClassification<V>();
        } else {
            grouping = new UniqueValueGrouping<V>();
        }
        grouping.setData(data);
        grouping.setNoOfClasses(noOfClasses);
        return grouping;
    }

    private JPanel buildTableChangerButtonBar() {
        ButtonBarBuilder bbBuilder = new ButtonBarBuilder();
        bbBuilder.addGriddedButtons(new JButton[] { addRowBt, removeRowBt });
        return bbBuilder.getPanel();
    }

    private void fillPropertyCB(List<QualifiedName> properties) {
        QualifiedName selectedQn = null;
        if (propertyCB.getSelectedItem() != null) {
            selectedQn = (QualifiedName) propertyCB.getSelectedItem();
        }
        propertyCB.removeAllItems();
        for (QualifiedName qn : properties) {
            propertyCB.addItem(qn);
            if (selectedQn != null && qn.equals(selectedQn)) {
                propertyCB.setSelectedItem(qn);
            }
        }
    }

    private void updateAfterClassificationChanged() {
        List<QualifiedName> pvList;
        boolean isClassesSpinnerEnabled = false;
        if (classificationTypeCB.getSelectedItem() == equalInterval) {
            pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE, Types.INTEGER,
                    Types.DATE, Types.BIGINT, Types.SMALLINT);
            isClassesSpinnerEnabled = true;
            numberOfClassesSpinner.setValue(6);
        } else if (classificationTypeCB.getSelectedItem() == quantile) {
            pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE, Types.INTEGER,
                    Types.DATE, Types.BIGINT, Types.SMALLINT);
            isClassesSpinnerEnabled = true;
            numberOfClassesSpinner.setValue(6);
        } else {
            pvList = assignedVisualPropPanel.getOwner().getPropertyNames();
        }
        isUpdating = true;
        fillPropertyCB(pvList);
        isUpdating = false;
        numberOfClassesSpinner.setEnabled(isClassesSpinnerEnabled);
        updateTable();
        numberOfClassesSpinner.setValue(model.getClassification().size());
        initColumnValues();
    }

    private void setClassificationActive() {
        if (!isInitialising) {
            status.setSelected(true);
            assignedVisualPropPanel.setActive(true);
        }
    }

    private void openHistogram() {
        if (propertyCB.getSelectedItem() != null) {
            String title = get("$MD11050", ((QualifiedName) propertyCB.getSelectedItem()).getLocalName());
            List<ValueRange<?>> values = new ArrayList<ValueRange<?>>();
            int dataSize = this.model.getThematicGrouping().getNumberOfData();
            int classes = this.model.getRowCount();
            if (dataSize / classes > 2) {
                for (ClassificationTableRow<?> row : this.model.getRows()) {
                    values.add(row.getValue());
                }
                histogram.update(title, values);
                openHistogramBt.setText(get("$MD11504"));
            } else {
                JOptionPane.showMessageDialog(this, get("$MD11530", dataSize, classes, 2), get("$MD11529"),
                        JOptionPane.INFORMATION_MESSAGE);
            }
        } else {
            JOptionPane.showMessageDialog(this, get("$MD11051"), get("$DI10018"), JOptionPane.INFORMATION_MESSAGE);
        }
    }

    private void addManuellClassificationItem() {
        if (classificationTypeCB.getSelectedItem() != uniqueValue
                && ((classificationTypeCB.getItemCount() == 0) || !isManual.isVisible())) {
            isManual.setVisible(true);
            numberOfClassesSpinner.setEnabled(false);
            propertyCB.setEnabled(false);
        }
    }

    private void removeManuellClassificationItem() {
        isManual.setVisible(false);
        numberOfClassesSpinner.setEnabled(true);
        propertyCB.setEnabled(true);
    }

    /**
     * transfers the values from the single elmements of the style to the classification
     */
    abstract public void initColumnValues();

    abstract protected List<Column> getColumns();

    abstract protected SYMBOLIZERTYPE getSymbolizerType();

    // //////////////////////////////////////////////////////////////////////////////
    // ACTIONLISTENER
    // //////////////////////////////////////////////////////////////////////////////

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(ActionEvent e) {
        Object src = e.getSource();
        if (src == classificationTypeCB) {
            if (isManual.isVisible()) {
                removeManuellClassificationItem();
            }
            unknownClassification.setEnabled(false);
            updateAfterClassificationChanged();
            setClassificationActive();
        } else if (src == propertyCB) {
            if (this.propertyCB.getSelectedItem() != null && !isUpdating) {
                updateTable();
            }
            setClassificationActive();
        } else if (src == addRowBt) {
            int row = classesTable.getSelectedRow();
            if (row < 0) {
                row = model.getRowCount();
            }
            model.addRowBefore(row);
            numberOfClassesSpinner.setValue(classesTable.getRowCount());
            setClassificationActive();
        } else if (src == removeRowBt) {
            // stop cell editing before removing selected rows
            int editingRow = classesTable.getEditingRow();
            int editingColumn = classesTable.getEditingColumn();
            if (editingRow > -1 && editingColumn > -1
                    && classesTable.getCellEditor(editingRow, editingColumn) != null) {
                classesTable.getCellEditor(editingRow, editingColumn).cancelCellEditing();
            }
            model.removeRows(classesTable.getSelectedRows());
            numberOfClassesSpinner.setValue(classesTable.getRowCount());
            setClassificationActive();
        } else if (src == openHistogramBt) {
            openHistogram();
        } else if (src == fullDatabase || src == extentDatabase) {
            updateTable();
        }
    }

    // //////////////////////////////////////////////////////////////////////////////
    // TABLEMODELLISTENER
    // //////////////////////////////////////////////////////////////////////////////

    public void tableChanged(TableModelEvent e) {
        // mark as manuell classification, if user changed a class limit in the table or
        // inserted/removed a row
        if ((model.getColumnIndex(VALUE) == e.getColumn() && !isManual.isVisible())
                || (e.getType() == TableModelEvent.INSERT) || (e.getType() == TableModelEvent.DELETE)) {
            addManuellClassificationItem();
        }
    }

    // //////////////////////////////////////////////////////////////////////////////
    // inner classes
    // //////////////////////////////////////////////////////////////////////////////

    private class ClassesTable extends JTable {

        private static final long serialVersionUID = 8303943959907931881L;

        @Override
        public TableCellRenderer getCellRenderer(int row, int column) {
            switch (model.getColumnType(column)) {
            case FILLCOLOR:
            case LINECOLOR:
            case FONTCOLOR:
            case HALOCOLOR:
                return new ColorTableCellRenderer();
            case SYMBOL:
                return new SymbolTableCellRenderer();
            case LINECAP:
                return new SldPropertyCellRenderer();
            default:
                return new LocaleTableCellRenderer();
            }
        }

        @Override
        public TableCellEditor getCellEditor(int row, int column) {
            TableCellEditor editor;
            switch (model.getColumnType(column)) {
            case FILLCOLOR:
                editor = new FillTableCellEditor(
                        assignedVisualPropPanel.getOwner().getSettings().getGraphicOptions(),
                        SldValues.getDefaultColor());
                break;
            case LINECOLOR:
            case HALOCOLOR:
            case FONTCOLOR:
                editor = new ColorTableCellEditor();
                break;
            case FILLTRANSPARENCY:
            case LINETRANSPARENCY:
            case FONTTRANSPARENCY:
                int opAsInt = SldValues.getOpacityInPercent(SldValues.getDefaultOpacity());
                editor = new SpinnerTableCellEditor(opAsInt, 0, 100, 1);
                break;
            case LINEWIDTH:
                editor = new SpinnerTableCellEditor(SldValues.getDefaultLineWidth(), 0.0, Integer.MAX_VALUE, 0.5);
                break;
            case LINESTYLE:
                JComboBox lineStyleCB = new JComboBox();
                int lineStyleCol = model.getColumnIndex(LINESTYLE);
                lineStyleCB.setRenderer(new DashArrayRenderer(
                        getTableHeader().getColumnModel().getColumn(lineStyleCol).getWidth(), 15));
                for (DashArray da : SldValues.getDashArrays()) {
                    lineStyleCB.addItem(da);
                }
                Map<String, DashArray> dashArrays = assignedVisualPropPanel.getOwner().getSettings()
                        .getGraphicOptions().getDashArrays();
                for (DashArray da : dashArrays.values()) {
                    lineStyleCB.addItem(da);
                }
                editor = new DefaultCellEditor(lineStyleCB);
                break;
            case SIZE:
                editor = new SpinnerTableCellEditor(SldValues.getDefaultSize(), 0.00001, Integer.MAX_VALUE, 1.0);
                break;
            case SYMBOL:
                JComboBox symbolCB = new JComboBox();
                symbolCB.setRenderer(new SymbolRenderer());
                for (WellKnownMark mark : SldValues.getWellKnownMarks()) {
                    symbolCB.addItem(mark);
                }
                try {
                    Map<String, GraphicSymbol> symbols = assignedVisualPropPanel.getOwner().getSettings()
                            .getGraphicOptions().getSymbolDefinitions();
                    List<GraphicSymbol> values = new ArrayList<GraphicSymbol>();
                    values.addAll(symbols.values());
                    Collections.sort((List<GraphicSymbol>) values);
                    for (GraphicSymbol symbol : values) {
                        symbolCB.addItem(symbol);
                    }
                } catch (MalformedURLException e) {
                    JOptionPane.showMessageDialog(this, get("$MD10788"), get("$DI10017"),
                            JOptionPane.ERROR_MESSAGE);
                }
                editor = new DefaultCellEditor(symbolCB);
                break;
            case LINECAP:
                JComboBox lineCapCB = new JComboBox();
                lineCapCB.setRenderer(new SldPropertyRenderer());
                for (SldProperty lc : SldValues.getLineCaps()) {
                    lineCapCB.addItem(lc);
                }
                editor = new DefaultCellEditor(lineCapCB);
                break;
            case FONTFAMILY:
                FontHelper fhf = new FontHelper();
                editor = new DefaultCellEditor(fhf.createFontFamilyChooser());
                break;
            case FONTSTYLE:
                FontHelper fhs = new FontHelper();
                editor = new DefaultCellEditor(fhs.createFontStyleChooser());
                break;
            case FONTWEIGHT:
                FontHelper fhw = new FontHelper();
                editor = new DefaultCellEditor(fhw.createFontWeightChooser());
                break;
            case FONTSIZE:
                editor = new SpinnerTableCellEditor(SldValues.getDefaultFontSize(), 1.0, Double.MAX_VALUE, 1.0);
                break;
            case HALORADIUS:
                editor = new SpinnerTableCellEditor(SldValues.getDefaultHaloRadius(), 0d, 50d, 1d);
                break;
            case ROTATION:
                editor = new SpinnerTableCellEditor(SldValues.getDefaultRotation(), 0.0, 360.0, 5.0d);
                break;
            case DISPLACEMENT:
                editor = new PointCellEditor(SldValues.getDefaultDisplacement());
                break;
            case ANCHORPOINT:
                editor = new PointCellEditor(SldValues.getDefaultAnchorPoint());
                break;
            default:
                return super.getCellEditor(row, column);
            }
            // add cell editor listener, to be informed, when cell editing was stopped or
            // canceled; was necessary, because of problems after deleting an editing row!
            editor.addCellEditorListener(this);
            return editor;
        }

        // Implement table header to show tool tips.
        @Override
        protected JTableHeader createDefaultTableHeader() {

            return new JTableHeader(columnModel) {

                private static final long serialVersionUID = 7937684822965281600L;

                public String getToolTipText(MouseEvent e) {
                    java.awt.Point p = e.getPoint();
                    int index = columnModel.getColumnIndexAtX(p.x);
                    int realIndex = columnModel.getColumn(index).getModelIndex();
                    return model.getColumnTooltip(realIndex);
                }
            };
        }
    };

}