private PlanAllocation buildPlanString(final IncompatiblePlanRecords incompatibleRecords,final List<PersonRecord> personsStillToAllocate,final Map<Id,PersonRecord> allPersons,final PlanAllocation currentAllocation,final Set<Id> allowedIncompatibilityGroups){
  final List<PersonRecord> remainingPersons=new ArrayList<PersonRecord>(personsStillToAllocate);
  final PersonRecord currentPerson=remainingPersons.remove(0);
  assert remainingPersons.size() == personsStillToAllocate.size() - 1;
  if (log.isTraceEnabled()) {
    log.trace("look at person " + currentPerson.person.getId() + " at level "+ (currentAllocation == null ? 0 : currentAllocation.getPlans().size()));
  }
  final List<PlanRecord> records=new ArrayList<PlanRecord>();
  for (  PlanRecord r : currentPerson.prunedPlans) {
    if (r.isStillFeasible)     records.add(r);
  }
  if (records.isEmpty())   return null;
  for (  PlanRecord r : records) {
    assert r.isStillFeasible;
    final PlanAllocation newAllocation=new PlanAllocation();
    newAllocation.add(r);
    List<PersonRecord> actuallyRemainingPersons=remainingPersons;
    if (r.jointPlan != null) {
      assert currentAllocation == null || !SelectorUtils.intersects(r.jointPlan.getIndividualPlans().keySet(),currentAllocation);
      assert r.linkedPlans.size() == r.jointPlan.getIndividualPlans().size() - 1;
      newAllocation.addAll(r.linkedPlans);
      actuallyRemainingPersons=SelectorUtils.filter(remainingPersons,r.jointPlan);
      assert actuallyRemainingPersons.size() + r.jointPlan.getIndividualPlans().size() == personsStillToAllocate.size();
    }
    if (!actuallyRemainingPersons.isEmpty()) {
      final FeasibilityChanger localFeasibilityChanger=new FeasibilityChanger();
      SelectorUtils.tagIncompatiblePlansAsInfeasible(r,incompatibleRecords,localFeasibilityChanger);
      if (currentAllocation != null)       currentAllocation.addAll(newAllocation.getPlans());
      final PlanAllocation record=buildPlanString(incompatibleRecords,actuallyRemainingPersons,allPersons,currentAllocation,newIncompatibilityGroups(incompatibleRecords,allowedIncompatibilityGroups,r));
      if (currentAllocation != null)       currentAllocation.removeAll(newAllocation.getPlans());
      localFeasibilityChanger.resetFeasibilities();
      if (record != null) {
        return SelectorUtils.merge(newAllocation,record);
      }
    }
 else {
      assert !forbidBlockingCombinations || currentAllocation != null;
      if (!forbidBlockingCombinations || !SelectorUtils.isBlocking(incompatibleRecords,allPersons,SelectorUtils.toGroupPlans(SelectorUtils.merge(currentAllocation,newAllocation)))) {
        return newAllocation;
      }
    }
  }
  return null;
}

private PlanAllocation buildPlanString ( final IncompatiblePlanRecords incompatibleRecords , final List < PersonRecord > personsStillToAllocate , final Map < Id , PersonRecord > allPersons , final PlanAllocation currentAllocation , final Set < Id > allowedIncompatibilityGroups ) { final List < PersonRecord > remainingPersons = new ArrayList < PersonRecord > ( personsStillToAllocate ) ; final PersonRecord currentPerson = remainingPersons . remove ( 0 ) ; assert remainingPersons . size ( ) == personsStillToAllocate . size ( ) - 1 ; if ( log . isTraceEnabled ( ) ) { log . trace ( "look at person " + currentPerson . person . getId ( ) + " at level " + ( currentAllocation == null ? 0 : currentAllocation . getPlans ( ) . size ( ) ) ) ; } final List < PlanRecord > records = new ArrayList < PlanRecord > ( ) ; for ( PlanRecord r : currentPerson . prunedPlans ) { if ( r . isStillFeasible ) records . add ( r ) ; } if ( records . isEmpty ( ) ) return null ; for ( PlanRecord r : records ) { assert r . isStillFeasible ; final PlanAllocation newAllocation = new PlanAllocation ( ) ; newAllocation . add ( r ) ; List < PersonRecord > actuallyRemainingPersons = remainingPersons ; if ( r . jointPlan != null ) { assert currentAllocation == null || ! SelectorUtils . intersects ( r . jointPlan . getIndividualPlans ( ) . keySet ( ) , currentAllocation ) ; assert r . linkedPlans . size ( ) == r . jointPlan . getIndividualPlans ( ) . size ( ) - 1 ; newAllocation . addAll ( r . linkedPlans ) ; actuallyRemainingPersons = SelectorUtils . filter ( remainingPersons , r . jointPlan ) ; assert actuallyRemainingPersons . size ( ) + r . jointPlan . getIndividualPlans ( ) . size ( ) == personsStillToAllocate . size ( ) ; } if ( ! actuallyRemainingPersons . isEmpty ( ) ) { final FeasibilityChanger localFeasibilityChanger = new FeasibilityChanger ( ) ; SelectorUtils . tagIncompatiblePlansAsInfeasible ( r , incompatibleRecords , localFeasibilityChanger ) ; if ( currentAllocation != null ) currentAllocation . addAll ( newAllocation . getPlans ( ) ) ; final PlanAllocation record = buildPlanString ( incompatibleRecords , actuallyRemainingPersons , allPersons , currentAllocation , newIncompatibilityGroups ( incompatibleRecords , allowedIncompatibilityGroups , r ) ) ; if ( currentAllocation != null ) currentAllocation . removeAll ( newAllocation . getPlans ( ) ) ; localFeasibilityChanger . resetFeasibilities ( ) ; if ( record != null ) { return SelectorUtils . merge ( newAllocation , record ) ; } } else { assert ! forbidBlockingCombinations || currentAllocation != null ; if ( ! forbidBlockingCombinations || ! SelectorUtils . isBlocking ( incompatibleRecords , allPersons , SelectorUtils . toGroupPlans ( SelectorUtils . merge ( currentAllocation , newAllocation ) ) ) ) { return newAllocation ; } } } return null ; }
recursively decends in the tree of possible joint plans .
