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.dialog.autorelationship;
  
  import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
  import org.apache.cayenne.map.DataMap;
  import org.apache.cayenne.map.DbAttribute;
  import org.apache.cayenne.map.DbEntity;
  import org.apache.cayenne.map.DbJoin;
  import org.apache.cayenne.map.DbRelationship;
  import org.apache.cayenne.modeler.util.CayenneController;
  
  import java.awt.*;
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  import java.util.function.Predicate;
  
  public class InferRelationshipsControllerBase extends CayenneController {
  
      public static final String SELECTED_PROPERTY = "selected";
  
      protected DataMap dataMap;
  
      protected List<InferredRelationship> inferredRelationships;
  
      protected List<DbEntity> entities;
      protected Set<InferredRelationship> selectedEntities;
      protected int index = 0;
      protected ObjectNameGenerator strategy;
  
      protected transient InferredRelationship currentEntity;
  
      public InferRelationshipsControllerBase(CayenneController parent, DataMap dataMap) {
          super(parent);
  
          this.dataMap = dataMap;
          this.entities = new ArrayList<>(dataMap.getDbEntities());
          this.selectedEntities = new HashSet<>();
      }
  
      public void setRelationships() {
          inferredRelationships = new ArrayList<>();
  
          for (DbEntity entity : entities) {
              createRelationships(entity);
          }
  
          createJoins();
          createNames();
      }
  
      protected void createRelationships(DbEntity entity) {
  
          for (DbAttribute attribute : entity.getAttributes()) {
  
              String name = attribute.getName();
              if (name.length() < 4) {
                  continue;
              }
  
              if (!name.substring(name.length() - 3, name.length()).equalsIgnoreCase("_ID")) {
                  continue;
              }
  
              String baseName = name.substring(0, name.length() - 3);
              for (DbEntity targetEntity : entities) {
                  // TODO: should we handle relationships to self??
                  if (targetEntity == entity) {
                      continue;
                  }
  
                  if (baseName.equalsIgnoreCase(targetEntity.getName())
                          && !attribute.isPrimaryKey()
                          && !targetEntity.getAttributes().isEmpty()) {
  
                      if (!attribute.isForeignKey()) {
                          InferredRelationship myir = new InferredRelationship();
                          myir.setSource(entity);
                          myir.setTarget(targetEntity);
                          inferredRelationships.add(myir);
                      }
                      createReversRelationship(targetEntity, entity);
                  }
              }
          }
      }
  
      public void createReversRelationship(DbEntity eSourse, DbEntity eTarget) {
          InferredRelationship myir = new InferredRelationship();
          for (DbRelationship relationship : eSourse.getRelationships()) {
              for (DbJoin join : relationship.getJoins()) {
                  if (((DbEntity) join.getSource().getEntity()).equals(eSourse)
                          && ((DbEntity) join.getTarget().getEntity()).equals(eTarget)) {
                      return;
                  }
              }
          }
          myir.setSource(eSourse);
          myir.setTarget(eTarget);
          inferredRelationships.add(myir);
      }
  
      public String getJoin(InferredRelationship irItem) {
          return irItem.getJoinSource().getName()
                  + " : "
                  + irItem.getJoinTarget().getName();
      }
  
      public String getToMany(InferredRelationship irItem) {
          if (irItem.isToMany()) {
              return "to many";
          }
          else {
              return "to one";
          }
      }
  
      protected DbAttribute getJoinAttribute(DbEntity sEntity, DbEntity tEntity) {
          if (sEntity.getAttributes().size() == 1) {
              return sEntity.getAttributes().iterator().next();
          }
          else {
              for (DbAttribute attr : sEntity.getAttributes()) {
                  if (attr.getName().equalsIgnoreCase(tEntity.getName() + "_ID")) {
                      return attr;
                  }
              }
  
              for (DbAttribute attr : sEntity.getAttributes()) {
                  if ((attr.getName().equalsIgnoreCase(sEntity.getName() + "_ID"))
                          && (!attr.isPrimaryKey())) {
                      return attr;
                  }
              }
  
              for (DbAttribute attr : sEntity.getAttributes()) {
                  if (attr.isPrimaryKey()) {
                      return attr;
                  }
              }
          }
          return null;
      }
  
      protected void createJoins() {
          Iterator<InferredRelationship> it = inferredRelationships.iterator();
          while (it.hasNext()) {
              InferredRelationship inferred = it.next();
  
              DbAttribute src = getJoinAttribute(inferred.getSource(), inferred.getTarget());
              if (src == null) {
                  // TODO: andrus 03/28/2010 this is pretty inefficient I guess... We should
                  // check for this condition earlier. See CAY-1405 for the map that caused
                  // this issue
                  it.remove();
                  continue;
              }
  
              DbAttribute target = getJoinAttribute(inferred.getTarget(), inferred
                      .getSource());
              if (target == null) {
                  // TODO: andrus 03/28/2010 this is pretty inefficient I guess... We should
                  // check for this condition earlier. See CAY-1405 for the map that caused
                  // this issue
                  it.remove();
                  continue;
              }
  
              inferred.setJoinSource(src);
              if (src.isPrimaryKey()) {
                  inferred.setToMany(true);
              }
  
              inferred.setJoinTarget(target);
          }
      }
  
      protected void createNames() {
  
  
          for (InferredRelationship myir : inferredRelationships) {
  
              DbRelationship localRelationship = new DbRelationship();
              localRelationship.setToMany(myir.isToMany());
  
              if (myir.getJoinSource().isPrimaryKey()) {
  
                  localRelationship.addJoin(
                          new DbJoin(localRelationship, myir.getJoinSource().getName(), myir.getJoinTarget().getName())
                  );
                  localRelationship.setSourceEntity(myir.getSource());
                  localRelationship.setTargetEntityName(myir.getTarget().getName());
              } else {
                  localRelationship.addJoin(
                          new DbJoin(localRelationship, myir.getJoinTarget().getName(), myir.getJoinSource().getName())
                  );
                  localRelationship.setSourceEntity(myir.getTarget());
                  localRelationship.setTargetEntityName(myir.getSource().getName());
              }
  
              myir.setName(strategy.relationshipName(localRelationship));
          }
      }
  
      public List<InferredRelationship> getSelectedEntities() {
          List<InferredRelationship> selected = new ArrayList<>(selectedEntities.size());
  
          for (InferredRelationship entity : inferredRelationships) {
              if (selectedEntities.contains(entity)) {
                  selected.add(entity);
              }
          }
  
          return selected;
      }
  
      public boolean updateSelection(Predicate<InferredRelationship> predicate) {
          boolean modified = false;
  
          for (InferredRelationship entity : inferredRelationships) {
              boolean select = predicate.test(entity);
  
              if (select) {
                  if (selectedEntities.add(entity)) {
                      modified = true;
                  }
              } else {
                  if (selectedEntities.remove(entity)) {
                      modified = true;
                  }
              }
          }
  
          if (modified) {
              firePropertyChange(SELECTED_PROPERTY, null, null);
          }
  
          return modified;
      }
  
      public boolean isSelected() {
          return currentEntity != null && selectedEntities.contains(currentEntity);
      }
  
      public void setSelected(boolean selectedFlag) {
          if (currentEntity == null) {
              return;
          }
  
          if (selectedFlag) {
              if (selectedEntities.add(currentEntity)) {
                  firePropertyChange(SELECTED_PROPERTY, null, null);
              }
          }
          else {
              if (selectedEntities.remove(currentEntity)) {
                  firePropertyChange(SELECTED_PROPERTY, null, null);
              }
          }
      }
  
      public int getSelectedEntitiesSize() {
          return selectedEntities.size();
      }
  
      public List getEntities() {
          return inferredRelationships;
      }
  
      public InferredRelationship getCurrentEntity() {
          return currentEntity;
      }
  
      public void setCurrentEntity(InferredRelationship currentEntity) {
          this.currentEntity = currentEntity;
      }
  
      public DataMap getDataMap() {
          return dataMap;
      }
  
      @Override
      public Component getView() {
          return null;
      }
  
      public void setNamingStrategy(ObjectNameGenerator namestr) {
          strategy = namestr;
      }
  
  }
  