package support; import java.util.*; /** * EfCoreAlgorithm — algorithmic models of EF Core CWE-407 defects. * * Three defects modelled: * * efcore-0001: PropertyExtensions.FindGenerationProperty() * BFS traversal of FK chains using List.Contains() for visited check. * O(D²) where D = FK chain depth. * * efcore-0002: IReadOnlyProperty.AddPrincipals() * Recursive traversal of principal chain using List.Contains(). * O(P²) where P = principal chain length. * * efcore-0003: ForeignKeyPropertyDiscoveryConvention * IReadOnlyList.Contains() called per (key_property, fk_property) pair * inside model-building loops. * O(K × Kp × Fp) where K=keys, Kp=key props, Fp=FK props. */ public class EfCoreAlgorithm { // ----------------------------------------------------------------------- // efcore-0001: FindGenerationProperty — BFS FK chain traversal // ----------------------------------------------------------------------- public static class Result { public final int defectiveOps; public final int fixedOps; public Result(int defectiveOps, int fixedOps) { this.defectiveOps = defectiveOps; this.fixedOps = fixedOps; } } /** * Defective: BFS traversal using List.Contains() — O(D²) visited check. * @param chainDepth number of FK hops in the principal chain * @return number of Contains() calls made */ public static int findGenerationPropertyDefective(int chainDepth) { // Simulate: traversalList = new List { seed } List traversalList = new ArrayList<>(); traversalList.add(0); int containsCalls = 0; int index = 0; while (index < traversalList.size()) { int current = traversalList.get(index); // Simulate: foreach FK { nextProperty = principalKey.Properties[...] } // One principal hop per step in the chain if (current < chainDepth - 1) { int next = current + 1; containsCalls++; // traversalList.Contains(next) if (!traversalList.contains(next)) { traversalList.add(next); } } index++; } return containsCalls; } /** * Fixed: BFS traversal using HashSet for O(1) visited check. * @param chainDepth number of FK hops * @return number of set-membership operations made (always 1 per step) */ public static int findGenerationPropertyFixed(int chainDepth) { List traversalList = new ArrayList<>(); Set traversalSet = new HashSet<>(); traversalList.add(0); traversalSet.add(0); int addCalls = 0; int index = 0; while (index < traversalList.size()) { int current = traversalList.get(index); if (current < chainDepth - 1) { int next = current + 1; addCalls++; // traversalSet.add(next) = O(1) if (traversalSet.add(next)) { traversalList.add(next); } } index++; } return addCalls; } // ----------------------------------------------------------------------- // efcore-0002: AddPrincipals — recursive principal chain dedup // ----------------------------------------------------------------------- /** * Defective: recursive traversal with List.Contains() — O(P²). * @param chainLength number of principal hops * @return total Contains() calls made across all recursion levels */ public static int addPrincipalsDefective(int chainLength) { List visited = new ArrayList<>(); visited.add(0); int[] callCount = {0}; addPrincipalsRecDefective(0, chainLength, visited, callCount); return callCount[0]; } private static void addPrincipalsRecDefective( int current, int chainLength, List visited, int[] callCount) { if (current >= chainLength - 1) return; int principal = current + 1; callCount[0]++; // visited.Contains(principal) if (!visited.contains(principal)) { visited.add(principal); addPrincipalsRecDefective(principal, chainLength, visited, callCount); } } /** * Fixed: recursive traversal with HashSet — O(P). * @param chainLength number of principal hops * @return total HashSet.add() calls */ public static int addPrincipalsFixed(int chainLength) { List principals = new ArrayList<>(); Set visited = new HashSet<>(); principals.add(0); visited.add(0); int[] callCount = {0}; addPrincipalsRecFixed(0, chainLength, principals, visited, callCount); return callCount[0]; } private static void addPrincipalsRecFixed( int current, int chainLength, List principals, Set visited, int[] callCount) { if (current >= chainLength - 1) return; int principal = current + 1; callCount[0]++; // visited.add(principal) — O(1) if (visited.add(principal)) { principals.add(principal); addPrincipalsRecFixed(principal, chainLength, principals, visited, callCount); } } // ----------------------------------------------------------------------- // efcore-0003: ForeignKeyPropertyDiscovery — key subset check // ----------------------------------------------------------------------- /** * Defective: IReadOnlyList.Contains() per (key_prop, fk_prop) pair — O(K×Kp×Fp). * @param numKeys number of keys to iterate * @param keyPropCount properties per key * @param fkPropCount properties in the FK (the list being searched) * @return total Contains() calls made */ public static int fkDiscoveryDefective(int numKeys, int keyPropCount, int fkPropCount) { // Simulate foreignKeyProperties as a List List foreignKeyProperties = new ArrayList<>(); for (int i = 0; i < fkPropCount; i++) foreignKeyProperties.add(i); int containsCalls = 0; for (int k = 0; k < numKeys; k++) { for (int kp = 0; kp < keyPropCount; kp++) { int prop = kp % fkPropCount; containsCalls++; // foreignKeyProperties.Contains(prop) = O(Fp) boolean found = foreignKeyProperties.contains(prop); if (!found) break; } } return containsCalls; } /** * Fixed: build HashSet once per FK, O(1) per contains check — O(K×Kp + Fp). */ public static int fkDiscoveryFixed(int numKeys, int keyPropCount, int fkPropCount) { List foreignKeyProperties = new ArrayList<>(); for (int i = 0; i < fkPropCount; i++) foreignKeyProperties.add(i); // Build the set once Set fkPropsSet = new HashSet<>(foreignKeyProperties); int containsCalls = 0; for (int k = 0; k < numKeys; k++) { for (int kp = 0; kp < keyPropCount; kp++) { int prop = kp % fkPropCount; containsCalls++; // fkPropsSet.contains(prop) = O(1) boolean found = fkPropsSet.contains(prop); if (!found) break; } } return containsCalls; } }