content: /*****************************************************************
   *   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.undo;
  
  import org.apache.cayenne.configuration.DataChannelDescriptor;
  import org.apache.cayenne.configuration.DataNodeDescriptor;
  import org.apache.cayenne.configuration.event.DataNodeEvent;
  import org.apache.cayenne.map.DataMap;
  import org.apache.cayenne.map.DbEntity;
  import org.apache.cayenne.map.DbRelationship;
  import org.apache.cayenne.map.Embeddable;
  import org.apache.cayenne.map.ObjEntity;
  import org.apache.cayenne.map.ObjRelationship;
  import org.apache.cayenne.map.Procedure;
  import org.apache.cayenne.map.QueryDescriptor;
  import org.apache.cayenne.modeler.Application;
  import org.apache.cayenne.modeler.ProjectController;
  import org.apache.cayenne.modeler.action.CreateDataMapAction;
  import org.apache.cayenne.modeler.action.CreateDbEntityAction;
  import org.apache.cayenne.modeler.action.CreateEmbeddableAction;
  import org.apache.cayenne.modeler.action.CreateNodeAction;
  import org.apache.cayenne.modeler.action.CreateObjEntityAction;
  import org.apache.cayenne.modeler.action.CreateProcedureAction;
  import org.apache.cayenne.modeler.action.CreateQueryAction;
  import org.apache.cayenne.modeler.action.CreateRelationshipAction;
  import org.apache.cayenne.modeler.action.RemoveAction;
  
  import javax.swing.undo.CannotRedoException;
  import javax.swing.undo.CannotUndoException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  public class RemoveUndoableEdit extends CayenneUndoableEdit {
  
      private DataMap map;
      private DbEntity dbEntity;
      private ObjEntity objEntity;
      private QueryDescriptor query;
      private Procedure procedure;
  
      private DataNodeDescriptor dataNode;
      private DataChannelDescriptor domain;
  
      private Embeddable embeddable;
  
      private final Map<DbEntity, List<DbRelationship>> dbRelationshipMap = new HashMap<>();
      private final Map<ObjEntity, List<ObjRelationship>> objRelationshipMap = new HashMap<>();
  
      private enum REMOVE_MODE {
          OBJECT_ENTITY, DB_ENTITY, QUERY, PROCEDURE, MAP_FROM_NODE, MAP_FROM_DOMAIN, NODE, DOMAIN, EMBEDDABLE
      }
  
      private final REMOVE_MODE mode;
  
      public RemoveUndoableEdit(Application application) {
          this.domain = (DataChannelDescriptor) application.getProject().getRootNode();
          this.mode = REMOVE_MODE.DOMAIN;
      }
  
      public RemoveUndoableEdit(Application application, DataNodeDescriptor node, DataMap map) {
          this.map = map;
          this.dataNode = node;
          this.mode = REMOVE_MODE.MAP_FROM_NODE;
      }
  
      public RemoveUndoableEdit(Application application, DataMap map) {
          this.domain = (DataChannelDescriptor) application.getProject().getRootNode();
          this.map = map;
          this.mode = REMOVE_MODE.MAP_FROM_DOMAIN;
      }
  
      public RemoveUndoableEdit(Application application, DataNodeDescriptor node) {
          this.domain = (DataChannelDescriptor) application.getProject().getRootNode();
          this.dataNode = node;
          this.mode = REMOVE_MODE.NODE;
      }
  
      public RemoveUndoableEdit(DataMap map, ObjEntity objEntity) {
          this.map = map;
          this.objEntity = objEntity;
          this.mode = REMOVE_MODE.OBJECT_ENTITY;
  
          for (ObjEntity ent : map.getObjEntities()) {
              // take a copy since we're going to modify the entity
              for (ObjRelationship relationship : new ArrayList<>(ent.getRelationships())) {
                  if (this.objEntity.getName().equals(relationship.getTargetEntityName())) {
                      objRelationshipMap.computeIfAbsent(relationship.getSourceEntity(), k -> new LinkedList<>());
                      objRelationshipMap.get(relationship.getSourceEntity()).add(relationship);
                  }
              }
          }
      }
  
      public RemoveUndoableEdit(DataMap map, DbEntity dbEntity) {
          this.map = map;
          this.dbEntity = dbEntity;
          this.mode = REMOVE_MODE.DB_ENTITY;
  
          for (ObjEntity objEnt : map.getObjEntities()) {
              for (ObjRelationship rel : objEnt.getRelationships()) {
                  for (DbRelationship dbRel : rel.getDbRelationships()) {
                      if (dbRel.getTargetEntity() == dbEntity) {
                          dbRelationshipMap.computeIfAbsent(dbRel.getSourceEntity(), k -> new LinkedList<>());
                          dbRelationshipMap.get(dbRel.getSourceEntity()).add(dbRel);
                          break;
                      }
                  }
              }
          }
      }
  
      public RemoveUndoableEdit(DataMap map, QueryDescriptor query) {
          this.map = map;
          this.query = query;
          this.mode = REMOVE_MODE.QUERY;
      }
  
      public RemoveUndoableEdit(DataMap map, Procedure procedure) {
          this.map = map;
          this.procedure = procedure;
          this.mode = REMOVE_MODE.PROCEDURE;
      }
  
      public RemoveUndoableEdit(DataMap map, Embeddable embeddable) {
          this.map = map;
          this.embeddable = embeddable;
          this.mode = REMOVE_MODE.EMBEDDABLE;
      }
  
      @Override
      public String getPresentationName() {
          switch (this.mode) {
              case OBJECT_ENTITY:
                  return "Remove Object Entity";
              case DB_ENTITY:
                  return "Remove Db Entity";
              case QUERY:
                  return "Remove Query";
              case PROCEDURE:
                  return "Remove Procedure";
              case MAP_FROM_NODE:
                  return "Remove DataMap";
              case MAP_FROM_DOMAIN:
                  return "Remove DataMap";
              case NODE:
                  return "Remove DataNode";
              case DOMAIN:
                  return "Remove DataDomain";
              case EMBEDDABLE:
                  return "Remove Embeddable";
              default:
                  return "Remove";
  
          }
      }
  
      @Override
      public void redo() throws CannotRedoException {
          RemoveAction action = actionManager.getAction(RemoveAction.class);
  
          switch (this.mode) {
              case OBJECT_ENTITY:
                  action.removeObjEntity(map, objEntity);
                  break;
              case DB_ENTITY:
                  action.removeDbEntity(map, dbEntity);
                  break;
              case QUERY:
                  action.removeQuery(map, query);
                  break;
              case PROCEDURE:
                  action.removeProcedure(map, procedure);
                  break;
              case MAP_FROM_NODE:
                  action.removeDataMapFromDataNode(dataNode, map);
                  break;
              case MAP_FROM_DOMAIN:
                  action.removeDataMap(map);
                  break;
              case NODE:
                  action.removeDataNode(dataNode);
                  break;
              case EMBEDDABLE:
                  action.removeEmbeddable(map, embeddable);
                  break;
          }
      }
  
      @Override
      public void undo() throws CannotUndoException {
  
          CreateRelationshipAction relationshipAction = actionManager.getAction(CreateRelationshipAction.class);
  
          switch (this.mode) {
              case OBJECT_ENTITY: {
                  for (Entry<ObjEntity, List<ObjRelationship>> entry : objRelationshipMap.entrySet()) {
                      ObjEntity objEntity = entry.getKey();
                      for (ObjRelationship rel : entry.getValue()) {
                          relationshipAction.createObjRelationship(objEntity, rel);
                      }
                  }
                  CreateObjEntityAction action = actionManager.getAction(CreateObjEntityAction.class);
                  action.createObjEntity(map, objEntity);
                  break;
              }
              case DB_ENTITY: {
                  for (Entry<DbEntity, List<DbRelationship>> entry : dbRelationshipMap.entrySet()) {
                      DbEntity dbEntity = entry.getKey();
                      for (DbRelationship rel : entry.getValue()) {
                          relationshipAction.createDbRelationship(dbEntity, rel);
                      }
                  }
                  CreateDbEntityAction action = actionManager.getAction(CreateDbEntityAction.class);
                  action.createEntity(map, dbEntity);
                  break;
              }
              case QUERY: {
                  this.domain = (DataChannelDescriptor) Application
                          .getInstance()
                          .getFrameController()
                          .getProjectController()
                          .getProject()
                          .getRootNode();
                  CreateQueryAction action = actionManager.getAction(CreateQueryAction.class);
                  action.createQuery(domain, map, query);
                  break;
              }
              case PROCEDURE: {
                  CreateProcedureAction action = actionManager.getAction(CreateProcedureAction.class);
                  action.createProcedure(map, procedure);
                  break;
              }
              case MAP_FROM_NODE: {
                  this.dataNode.getDataMapNames().add(map.getName());
                  ProjectController controller = Application.getInstance().getFrameController().getProjectController();
                  DataNodeEvent e = new DataNodeEvent(Application.getFrame(), this.dataNode);
                  e.setDomain((DataChannelDescriptor) controller.getProject().getRootNode());
                  controller.fireDataNodeEvent(e);
                  break;
              }
              case MAP_FROM_DOMAIN: {
                  CreateDataMapAction action = actionManager.getAction(CreateDataMapAction.class);
                  action.createDataMap(map);
                  break;
              }
              case NODE: {
                  CreateNodeAction action = actionManager.getAction(CreateNodeAction.class);
                  action.createDataNode(dataNode);
                  break;
              }
              case EMBEDDABLE: {
                  CreateEmbeddableAction action = actionManager.getAction(CreateEmbeddableAction.class);
                  action.createEmbeddable(map, embeddable);
                  break;
              }
          }
      }
  }
  