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

org.apache.cayenne.modeler.util.PathChooserComboBoxCellEditor Maven / Gradle / Ivy

There is a newer version: 5.0-M1
Show newest version
/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/

package org.apache.cayenne.modeler.util;

import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.modeler.Application;
import org.apache.cayenne.modeler.util.combo.AutoCompletion;
import org.apache.cayenne.util.Util;

import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.TableCellEditor;
import javax.swing.text.JTextComponent;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * This class used as cell editor, when you need to
 * choose path in comboBox and use autocompletion.
 */
@SuppressWarnings("WeakerAccess")
public abstract class PathChooserComboBoxCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, PopupMenuListener {

    protected JComboBox comboBoxPathChooser;
    protected int previousEmbeddedLevel = 0;
    protected EntityTreeModel treeModel;
    protected int row;
    private JTable table;

    protected abstract void enterPressed(JTable table);

    protected abstract EntityTreeModel createTreeModelForComboBox(int indexInTable);

    protected abstract Object getCurrentNodeToInitializeCombo(CayenneTableModel model, int row);

    protected abstract String getPathToInitializeCombo(CayenneTableModel model, int row);

    protected void initializeCombo(CayenneTableModel model, int row, final JTable table) {
        Object currentNode = getCurrentNodeToInitializeCombo(model, row);
        String dbAttributePath = getPathToInitializeCombo(model, row);
        List nodeChildren = getChildren(currentNode, dbAttributePath);
        this.table = table;
        comboBoxPathChooser = Application.getWidgetFactory().createComboBox(
                nodeChildren,
                false);
        comboBoxPathChooser.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent event) {
                if (event.getKeyCode() == KeyEvent.VK_ENTER) {
                    enterPressed(table);
                    return;
                }
                parsePathString(event.getKeyChar());
            }
        });
        AutoCompletion.enable(comboBoxPathChooser, true, true);
        ((JComponent) comboBoxPathChooser.getEditor().getEditorComponent()).setBorder(null);
        comboBoxPathChooser.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
        comboBoxPathChooser.setRenderer(new PathChooserComboBoxCellRenderer());
        comboBoxPathChooser.addActionListener(this);
        comboBoxPathChooser.addPopupMenuListener(this);
    }

    private void setComboModelAccordingToPath(String pathString) {
        List currentNodeChildren = new ArrayList<>(getChildren(getCurrentNode(pathString), pathString));
        comboBoxPathChooser.setModel(new DefaultComboBoxModel<>(currentNodeChildren.toArray(new String[0])));
        comboBoxPathChooser.setSelectedItem(pathString);
        if(!pathString.isEmpty()) {
            comboBoxPathChooser.showPopup();
        }
    }

    protected void parsePathString(char lastEnteredCharacter) {
        JTextComponent editorComponent = (JTextComponent) (comboBoxPathChooser).getEditor().getEditorComponent();
        String pathString = editorComponent.getText();
        if (pathString != null && pathString.isEmpty()) {
            setComboModelAccordingToPath("");
            previousEmbeddedLevel = 0;
            return;
        }

        if (lastEnteredCharacter == '.') {
            processDotEntered();
            previousEmbeddedLevel = Util.countMatches(pathString, ".");
            return;
        }

        int currentEmbeddedLevel = Util.countMatches(pathString, ".");
        if (previousEmbeddedLevel != currentEmbeddedLevel) {
            previousEmbeddedLevel = currentEmbeddedLevel;
            String[] pathStrings = pathString.split(Pattern.quote("."));
            String lastStringInPath = pathStrings[pathStrings.length - 1];
            String saveDbAttributePath = pathString;
            pathString = pathString.replaceAll(lastStringInPath + "$", "");
            List currentNodeChildren = new ArrayList<>(getChildren(getCurrentNode(pathString), pathString));
            comboBoxPathChooser.setModel(new DefaultComboBoxModel<>(currentNodeChildren.toArray(new String[0])));
            comboBoxPathChooser.setSelectedItem(saveDbAttributePath);
        }
    }

    protected void processDotEntered() {
        JTextComponent editorComponent = (JTextComponent) (comboBoxPathChooser).getEditor().getEditorComponent();

        String dbAttributePath = editorComponent.getText();
        if (".".equals(dbAttributePath)) {
            setComboModelAccordingToPath("");
            return;
        }
        char secondFromEndCharacter = dbAttributePath.charAt(dbAttributePath.length() - 2);
        if (secondFromEndCharacter == '.') {
            // two dots entered one by one , we replace it by one dot
            editorComponent.setText(dbAttributePath.substring(0, dbAttributePath.length() - 1));
            return;
        }
        String[] pathStrings = dbAttributePath.split(Pattern.quote("."));
        String lastStringInPath = pathStrings[pathStrings.length - 1];

        //we will check if lastStringInPath is correct name of DbAttribute or DbRelationship
        //for appropriate previous node in path. if it is not we won't add entered dot to dbAttributePath
        String dbAttributePathForPreviousNode;
        if (pathStrings.length == 1) {
            //previous root is treeModel.getRoot()
            dbAttributePathForPreviousNode = "";
        } else {
            dbAttributePathForPreviousNode = dbAttributePath.replaceAll('.' + lastStringInPath + ".$", "");
        }
        List potentialVariantsToChoose = getChildren(getCurrentNode(dbAttributePathForPreviousNode), "");
        if (potentialVariantsToChoose.contains(lastStringInPath) &&
                !(getCurrentNode(dbAttributePath) instanceof DbAttribute)) {
            setComboModelAccordingToPath(dbAttributePath);
        } else {
            editorComponent.setText(dbAttributePath.substring(0, dbAttributePath.length() - 1));
        }
    }

    /**
     * find current node by path
     *
     * @param pathString db path
     * @return last node in path which matches DbRelationship or DbAttribute
     */
    protected Object getCurrentNode(String pathString) {
        //case for new attribute
        if (pathString == null || pathString.isEmpty()) {
            return treeModel.getRoot();
        }
        String[] pathStrings = pathString.split(Pattern.quote("."));
        Object root = treeModel.getRoot();
        for (String rootChildText : pathStrings) {
            for (int j = 0; j < treeModel.getChildCount(root); j++) {
                Object child = treeModel.getChild(root, j);
                String objectName = ModelerUtil.getObjectName(child);
                if (objectName.equals(rootChildText)) {
                    root = child;
                    break;
                }
            }
        }
        return root;
    }

    /**
     * @param node       for which we will find children
     * @param pathString string which will be added to each child to make right autocomplete
     * @return list with children , which will be used to autocomplete
     */
    protected List getChildren(Object node, String pathString) {
        List currentNodeChildren = new ArrayList<>();
        for (int j = 0; j < treeModel.getChildCount(node); j++) {
            Object child = treeModel.getChild(node, j);
            String relationshipName = ModelerUtil.getObjectName(child);
            currentNodeChildren.add(pathString + relationshipName);
        }
        return currentNodeChildren;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //for some reason comboBoxPathChooser don't load selected item text, so we made it by hand
        if (comboBoxPathChooser.getSelectedIndex() != (-1)) {
            ((JTextComponent) (comboBoxPathChooser).
                    getEditor().getEditorComponent()).setText(comboBoxPathChooser.getSelectedItem().toString());
        }
    }

    @Override
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        if (comboBoxPathChooser.getSelectedIndex() != -1 &&
                !((JTextComponent) (comboBoxPathChooser).
                        getEditor().getEditorComponent()).getText().isEmpty()) {
            enterPressed(table);
        }
    }

    @Override
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    }

    @Override
    public void popupMenuCanceled(PopupMenuEvent e) {
    }

    private final class PathChooserComboBoxCellRenderer extends DefaultListCellRenderer {

        private  final ImageIcon rightArrow = ModelerUtil.buildIcon("icon-arrow-closed.png");

        @SuppressWarnings("unchecked")
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index,
                                                      boolean isSelected, boolean cellHasFocus) {

            JPanel panel = new JPanel(new BorderLayout());
            JLabel label = new JLabel((String) value);
            panel.add(label);

            Object currentNode = getCurrentNode((String) value);
            if (treeModel.isLeaf(currentNode)) {
                ListCellRenderer leafRenderer = CellRenderers.listRenderer();
                return leafRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            } else {
                DefaultListCellRenderer nonLeafTextRenderer = new DefaultListCellRenderer();
                Component text = nonLeafTextRenderer.getListCellRendererComponent(list, value, index, isSelected,
                        cellHasFocus);
                panel.setBackground(text.getBackground());
                panel.add(new JLabel(rightArrow), BorderLayout.EAST);
                return panel;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy