public static List<TransitionTarget> getRelocateTargets(
      long activityInstanceOid, TransitionOptions options, ScanDirection direction) {
    IActivityInstance ai = ActivityInstanceBean.findByOID(activityInstanceOid);
    IActivity activity = ai.getActivity();

    if (!activity.getBooleanAttribute(PredefinedConstants.ACTIVITY_IS_RELOCATE_SOURCE_ATT)) {
      // activity is not a relocation source
      return Collections.emptyList();
    }

    Stack<TransitionStep> steps = new Stack();
    List<TransitionTarget> targets = CollectionUtils.newList();
    Set<TransitionTarget> visited = CollectionUtils.newSet();
    switch (direction) {
      case FORWARD:
        addActivities(
            visited,
            targets,
            ai,
            options == null ? TransitionOptions.DEFAULT : options,
            true,
            steps);
        break;
      case BACKWARD:
        addActivities(
            visited,
            targets,
            ai,
            options == null ? TransitionOptions.DEFAULT : options,
            false,
            steps);
        break;
      default:
        addActivities(
            visited,
            targets,
            ai,
            options == null ? TransitionOptions.DEFAULT : options,
            true,
            steps);
        addActivities(
            visited,
            targets,
            ai,
            options == null ? TransitionOptions.DEFAULT : options,
            false,
            steps);
    }
    return targets;
  }
  private static void addActivity(
      Set<TransitionTarget> visited,
      List<TransitionTarget> targets,
      IActivity target,
      TransitionOptions options,
      boolean forward,
      Stack<TransitionStep> steps) {
    TransitionTarget candidate =
        TransitionTargetFactory.createTransitionTarget(target, steps, forward);
    if (!visited.add(candidate)) {
      // target already visited, stop processing
      return;
    }
    if (target.getBooleanAttribute(PredefinedConstants.ACTIVITY_IS_RELOCATE_TARGET_ATT)) {
      // found a relocation target, check filters
      String processIdPattern = options.getProcessIdPattern();
      if (processIdPattern == null
          || target.getProcessDefinition().getId().matches(processIdPattern)) {
        String activityIdPattern = options.getActivityIdPattern();
        if (activityIdPattern == null || target.getId().matches(activityIdPattern)) {
          targets.add(candidate);
        }
      }
    }

    if (options.isTransitionIntoSubprocessesAllowed()
        && target.getImplementationType() == ImplementationType.SubProcess
        && target.getSubProcessMode() != SubProcessModeKey.ASYNC_SEPARATE) {
      IProcessDefinition process = target.getImplementationProcessDefinition();
      if (process != null) {
        steps.push(TransitionTargetFactory.createTransitionStep(target));
        addActivities(visited, targets, process, options, forward, steps);
        steps.pop();
      }
    }

    addActivities(visited, targets, target, options, forward, steps);
  }