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

org.apache.cayenne.modeler.action.FindAction Maven / Gradle / Ivy

/*****************************************************************
 *   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.action;

import org.apache.cayenne.configuration.DataChannelDescriptor;
import org.apache.cayenne.dbsync.reverse.dbload.DbRelationshipDetected;
import org.apache.cayenne.map.Attribute;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.dbsync.model.DetectedDbEntity;
import org.apache.cayenne.map.EJBQLQueryDescriptor;
import org.apache.cayenne.map.Embeddable;
import org.apache.cayenne.map.EmbeddableAttribute;
import org.apache.cayenne.map.Entity;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.map.Procedure;
import org.apache.cayenne.map.ProcedureParameter;
import org.apache.cayenne.map.ProcedureQueryDescriptor;
import org.apache.cayenne.map.Relationship;
import org.apache.cayenne.map.SQLTemplateDescriptor;
import org.apache.cayenne.map.SelectQueryDescriptor;
import org.apache.cayenne.modeler.Application;
import org.apache.cayenne.modeler.CayenneModelerFrame;
import org.apache.cayenne.modeler.ProjectTreeModel;
import org.apache.cayenne.modeler.ProjectTreeView;
import org.apache.cayenne.modeler.dialog.FindDialog;
import org.apache.cayenne.modeler.editor.EditorView;
import org.apache.cayenne.modeler.event.AttributeDisplayEvent;
import org.apache.cayenne.modeler.event.EmbeddableAttributeDisplayEvent;
import org.apache.cayenne.modeler.event.EmbeddableDisplayEvent;
import org.apache.cayenne.modeler.event.EntityDisplayEvent;
import org.apache.cayenne.modeler.event.ProcedureDisplayEvent;
import org.apache.cayenne.modeler.event.ProcedureParameterDisplayEvent;
import org.apache.cayenne.modeler.event.QueryDisplayEvent;
import org.apache.cayenne.modeler.event.RelationshipDisplayEvent;
import org.apache.cayenne.modeler.util.CayenneAction;
import org.apache.cayenne.map.QueryDescriptor;

import javax.swing.JTextField;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

public class FindAction extends CayenneAction {

    /**
     * Result sort priority based on result type
     */
    private static final Map, Integer> PRIORITY_BY_TYPE = new HashMap<>();
    static {
        PRIORITY_BY_TYPE.put(ObjEntity.class,                1);
        PRIORITY_BY_TYPE.put(DbEntity.class,                 2);
        PRIORITY_BY_TYPE.put(DetectedDbEntity.class,         2); // this one comes from db reverse engineering
        PRIORITY_BY_TYPE.put(ObjAttribute.class,             5);
        PRIORITY_BY_TYPE.put(DbAttribute.class,              6);
        PRIORITY_BY_TYPE.put(ObjRelationship.class,          7);
        PRIORITY_BY_TYPE.put(DbRelationship.class,           8);
        PRIORITY_BY_TYPE.put(DbRelationshipDetected.class,   8); // this one comes from db reverse engineering
        PRIORITY_BY_TYPE.put(QueryDescriptor.class,          9);
        PRIORITY_BY_TYPE.put(SelectQueryDescriptor.class,   10);
        PRIORITY_BY_TYPE.put(EJBQLQueryDescriptor.class,    11);
        PRIORITY_BY_TYPE.put(SQLTemplateDescriptor.class,   12);
        PRIORITY_BY_TYPE.put(ProcedureQueryDescriptor.class,13);
        PRIORITY_BY_TYPE.put(Embeddable.class,              14);
        PRIORITY_BY_TYPE.put(EmbeddableAttribute.class,     15);
        PRIORITY_BY_TYPE.put(Procedure.class,               16);
        PRIORITY_BY_TYPE.put(ProcedureParameter.class,      17);
    }

    public static String getActionName() {
        return "Find";
    }

    public FindAction(Application application) {
        super(getActionName(), application);
    }

    /**
     * All entities that contain a pattern substring (case-indifferent) in the name are produced.
     */
    public void performAction(ActionEvent e) {
        JTextField source = (JTextField) e.getSource();
        String searchStr = source.getText().trim();
        if (searchStr.startsWith("*")) {
            searchStr = searchStr.substring(1);
        }

        if(searchStr.isEmpty()) {
            markEmptySearch(source);
            return;
        }

        List searchResults = search(searchStr);
        if(searchResults.isEmpty()){
            markEmptySearch(source);
        } else if(searchResults.size() == 1){
            jumpToResult(searchResults.iterator().next());
        } else {
            new FindDialog(getApplication().getFrameController(), searchResults).startupAction();
        }
    }

    private void markEmptySearch(JTextField source) {
        source.setBackground(Color.pink);
    }

    /**
     * Navigate to search result
     * Used also in {@link org.apache.cayenne.modeler.graph.action.EntityDisplayAction}
     */
    public static void jumpToResult(FindAction.SearchResultEntry searchResultEntry) {
        EditorView editor = ((CayenneModelerFrame) Application.getInstance().getFrameController().getView()).getView();
        DataChannelDescriptor domain = (DataChannelDescriptor) Application.getInstance().getProject().getRootNode();

        if (searchResultEntry.getObject() instanceof Entity) {
            jumpToEntityResult((Entity) searchResultEntry.getObject(), editor, domain);
        } else if (searchResultEntry.getObject() instanceof QueryDescriptor) {
            jumpToQueryResult((QueryDescriptor)searchResultEntry.getObject(), editor, domain);
        } else if (searchResultEntry.getObject() instanceof Embeddable) {
            jumpToEmbeddableResult((Embeddable)searchResultEntry.getObject(), editor, domain);
        } else if (searchResultEntry.getObject() instanceof EmbeddableAttribute) {
            jumpToEmbeddableAttributeResult((EmbeddableAttribute)searchResultEntry.getObject(), editor, domain);
        } else if (searchResultEntry.getObject() instanceof Attribute || searchResultEntry.getObject() instanceof Relationship) {
            jumpToAttributeResult(searchResultEntry, editor, domain);
        } else if (searchResultEntry.getObject() instanceof Procedure) {
            jumpToProcedureResult((Procedure)searchResultEntry.getObject(), editor, domain);
        } else if (searchResultEntry.getObject() instanceof ProcedureParameter) {
            jumpToProcedureResult((ProcedureParameter)searchResultEntry.getObject(), editor, domain);
        }
    }

    private List search(String searchStr) {
        Pattern pattern = Pattern.compile(searchStr, Pattern.CASE_INSENSITIVE);
        List result = new ArrayList<>();
        for (DataMap dataMap : ((DataChannelDescriptor) getProjectController().getProject().getRootNode()).getDataMaps()) {
            searchInQueryDescriptors(pattern, result, dataMap);
            searchInEmbeddables(pattern, result, dataMap);
            searchInDbEntities(pattern, result, dataMap);
            searchInObjEntities(pattern, result, dataMap);
            searchInProcedures(pattern, result, dataMap);
        }
        Collections.sort(result);
        return result;
    }

    private void searchInQueryDescriptors(Pattern pattern, List result, DataMap dataMap) {
        for (QueryDescriptor q : dataMap.getQueryDescriptors()) {
            if (match(q.getName(), pattern)) {
                result.add(new SearchResultEntry(q, q.getName()));
            }
        }
    }

    private void searchInEmbeddables(Pattern pattern, List result, DataMap dataMap) {
        for (Embeddable emb : dataMap.getEmbeddables()) {
            if (match(emb.getClassName(), pattern)) {
                result.add(new SearchResultEntry(emb, emb.getClassName()));
            }
            for (EmbeddableAttribute attr : emb.getAttributes()) {
                if (match(attr.getName(), pattern)) {
                    result.add(new SearchResultEntry(attr, emb.getClassName() + "." + attr.getName()));
                }
            }
        }
    }

    private void searchInObjEntities(Pattern pattern, List result, DataMap dataMap) {
        for (ObjEntity ent : dataMap.getObjEntities()) {
            if (match(ent.getName(), pattern)) {
                result.add(new SearchResultEntry(ent, ent.getName()));
            }
            for (ObjAttribute attr : ent.getAttributes()) {
                if (match(attr.getName(), pattern)) {
                    result.add(new SearchResultEntry(attr, ent.getName() + "." + attr.getName()));
                }
            }
            for (ObjRelationship rel : ent.getRelationships()) {
                if (match(rel.getName(), pattern)) {
                    result.add(new SearchResultEntry(rel, ent.getName() + "." + rel.getName()));
                }
            }
        }
    }

    private void searchInDbEntities(Pattern pattern, List result, DataMap dataMap) {
        for (DbEntity ent : dataMap.getDbEntities()) {
            if (match(ent.getName(), pattern)) {
                result.add(new SearchResultEntry(ent, ent.getName()));
            }
            for (DbAttribute attr : ent.getAttributes()) {
                if (match(attr.getName(), pattern)) {
                    result.add(new SearchResultEntry(attr, ent.getName() + "." + attr.getName()));
                }
            }
            for (DbRelationship rel : ent.getRelationships()) {
                if (match(rel.getName(), pattern)) {
                    result.add(new SearchResultEntry(rel, ent.getName() + "." + rel.getName()));
                }
            }

            checkCatalogOrSchema(pattern, result, ent, ent.getCatalog());
            checkCatalogOrSchema(pattern, result, ent, ent.getSchema());
        }
    }

    private void searchInProcedures(Pattern pattern, List result, DataMap dataMap) {
        for (Procedure proc : dataMap.getProcedures()) {
            if (match(proc.getName(), pattern)) {
                result.add(new SearchResultEntry(proc, proc.getName()));
            }

            for(ProcedureParameter param : proc.getCallParameters()) {
                if(match(param.getName(), pattern)) {
                    result.add(new SearchResultEntry(param, proc.getName() + '.' + param.getName()));
                }
            }
        }
    }

    private void checkCatalogOrSchema(Pattern pattern, List paths, DbEntity ent, String catalogOrSchema) {
        if (catalogOrSchema != null && !catalogOrSchema.isEmpty()) {
            if (match(catalogOrSchema, pattern)) {
                SearchResultEntry entry = new SearchResultEntry(ent, ent.getName());
                if(!paths.contains(entry)) {
                    paths.add(entry);
                }
            }
        }
    }

    private boolean match(String entityName, Pattern pattern) {
        return pattern.matcher(entityName).find();
    }

    private static void jumpToAttributeResult(SearchResultEntry searchResultEntry, EditorView editor, DataChannelDescriptor domain) {
        DataMap map;
        Entity entity;
        if (searchResultEntry.getObject() instanceof Attribute) {
            map = ((Attribute) searchResultEntry.getObject()).getEntity().getDataMap();
            entity = ((Attribute) searchResultEntry.getObject()).getEntity();
        } else {
            map = ((Relationship) searchResultEntry.getObject()).getSourceEntity().getDataMap();
            entity = ((Relationship) searchResultEntry.getObject()).getSourceEntity();
        }
        buildAndSelectTreePath(map, entity, editor);

        if (searchResultEntry.getObject() instanceof Attribute) {
            AttributeDisplayEvent event = new AttributeDisplayEvent(editor.getProjectTreeView(),
                    (Attribute) searchResultEntry.getObject(), entity, map, domain);
            event.setMainTabFocus(true);
            if(searchResultEntry.getObject() instanceof DbAttribute) {
                editor.getDbDetailView().currentDbAttributeChanged(event);
                editor.getDbDetailView().repaint();
            } else {
                editor.getObjDetailView().currentObjAttributeChanged(event);
                editor.getObjDetailView().repaint();
            }
        } else if (searchResultEntry.getObject() instanceof Relationship) {
            RelationshipDisplayEvent event = new RelationshipDisplayEvent(editor.getProjectTreeView(),
                    (Relationship) searchResultEntry.getObject(), entity, map, domain);
            event.setMainTabFocus(true);
            if(searchResultEntry.getObject() instanceof DbRelationship) {
                editor.getDbDetailView().currentDbRelationshipChanged(event);
                editor.getDbDetailView().repaint();
            } else {
                editor.getObjDetailView().currentObjRelationshipChanged(event);
                editor.getObjDetailView().repaint();
            }
        }
    }

    private static void jumpToEmbeddableAttributeResult(EmbeddableAttribute attribute, EditorView editor, DataChannelDescriptor domain) {
        Embeddable embeddable = attribute.getEmbeddable();
        DataMap map = embeddable.getDataMap();
        buildAndSelectTreePath(map, embeddable, editor);
        EmbeddableAttributeDisplayEvent event = new EmbeddableAttributeDisplayEvent(editor.getProjectTreeView(),
                embeddable, attribute, map, domain);
        event.setMainTabFocus(true);
        editor.getEmbeddableView().currentEmbeddableAttributeChanged(event);
        editor.getEmbeddableView().repaint();
    }

    private static void jumpToEmbeddableResult(Embeddable embeddable, EditorView editor, DataChannelDescriptor domain) {
        DataMap map = embeddable.getDataMap();
        buildAndSelectTreePath(map, embeddable, editor);
        EmbeddableDisplayEvent event = new EmbeddableDisplayEvent(editor.getProjectTreeView(), embeddable, map, domain);
        event.setMainTabFocus(true);
        editor.currentEmbeddableChanged(event);
    }

    private static void jumpToQueryResult(QueryDescriptor queryDescriptor, EditorView editor, DataChannelDescriptor domain) {
        DataMap map = queryDescriptor.getDataMap();
        buildAndSelectTreePath(map, queryDescriptor, editor);
        QueryDisplayEvent event = new QueryDisplayEvent(editor.getProjectTreeView(), queryDescriptor, map, domain);
        editor.currentQueryChanged(event);
    }

    private static void jumpToEntityResult(Entity entity, EditorView editor, DataChannelDescriptor domain) {
        DataMap map = entity.getDataMap();
        buildAndSelectTreePath(map, entity, editor);
        EntityDisplayEvent event = new EntityDisplayEvent(editor.getProjectTreeView(), entity, map, domain);
        event.setMainTabFocus(true);

        if (entity instanceof ObjEntity) {
            editor.getObjDetailView().currentObjEntityChanged(event);
        } else if (entity instanceof DbEntity) {
            editor.getDbDetailView().currentDbEntityChanged(event);
        }
    }

    private static void jumpToProcedureResult(Procedure procedure, EditorView editor, DataChannelDescriptor domain) {
        DataMap map = procedure.getDataMap();
        buildAndSelectTreePath(map, procedure, editor);
        ProcedureDisplayEvent event = new ProcedureDisplayEvent(editor.getProjectTreeView(), procedure, map, domain);
        editor.getProcedureView().currentProcedureChanged(event);
        editor.getProcedureView().repaint();
    }

    private static void jumpToProcedureResult(ProcedureParameter parameter, EditorView editor, DataChannelDescriptor domain) {
        Procedure procedure = parameter.getProcedure();
        DataMap map = procedure.getDataMap();
        buildAndSelectTreePath(map, procedure, editor);
        ProcedureParameterDisplayEvent event =
                new ProcedureParameterDisplayEvent(editor.getProjectTreeView(), parameter, procedure, map, domain);
        editor.getProcedureView().currentProcedureParameterChanged(event);
        editor.getProcedureView().repaint();
    }

    /**
     * Builds a tree path for a given path and make selection in it
     */
    private static TreePath buildAndSelectTreePath(DataMap map, Object object, EditorView editor) {
        ProjectTreeView projectTreeView = editor.getProjectTreeView();
        ProjectTreeModel treeModel = (ProjectTreeModel) projectTreeView.getModel();

        DefaultMutableTreeNode[] mutableTreeNodes = new DefaultMutableTreeNode[] {
            treeModel.getRootNode(),
            treeModel.getNodeForObjectPath(new Object[]{map}),
            treeModel.getNodeForObjectPath(new Object[]{map, object})
        };

        TreePath treePath = new TreePath(mutableTreeNodes);
        if (!projectTreeView.isExpanded(treePath.getParentPath())) {
            projectTreeView.expandPath(treePath.getParentPath());
        }
        projectTreeView.getSelectionModel().setSelectionPath(treePath);
        return treePath;
    }

    /**
     * Search result holder
     */
    public static class SearchResultEntry implements Comparable{
        private final Object object;
        private final String name;

        public SearchResultEntry(Object object, String name) {
            this.object = Objects.requireNonNull(object);
            this.name = Objects.requireNonNull(name);
        }

        public String getName() {
            return name;
        }

        public Object getObject() {
            return object;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            SearchResultEntry entry = (SearchResultEntry) o;
            return name.equals(entry.name) && object.getClass().equals(entry.object.getClass());
        }

        @Override
        public int hashCode() {
            int result = object.getClass().hashCode();
            result = 31 * result + name.hashCode();
            return result;
        }

        private int getPriority() {
            Integer priority = PRIORITY_BY_TYPE.get(object.getClass());
            if(priority == null) {
                throw new NullPointerException("Unknown type: " + object.getClass().getCanonicalName());
            }
            return priority;
        }

        @Override
        public int compareTo(SearchResultEntry o) {
            int res = getPriority() - o.getPriority();
            if(res != 0) {
                return res;
            }
            return getName().compareTo(o.getName());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy