@Override
  public PartLink filterPartLink(List<PartLink> path) {

    // No ambiguities here, must return 1 value
    // Check if optional or substitute, nominal link else

    PartLink nominalLink = path.get(path.size() - 1);

    if (nominalLink.isOptional() && optionalUsageLinks.contains(Tools.getPathAsString(path))) {
      retainedOptionalUsageLinks.add(Tools.getPathAsString(path));
      return null;
    }

    for (PartSubstituteLink substituteLink : nominalLink.getSubstitutes()) {

      List<PartLink> substitutePath = new ArrayList<>(path);
      substitutePath.set(substitutePath.size() - 1, substituteLink);

      if (substituteLinks.contains(Tools.getPathAsString(substitutePath))) {
        retainedSubstituteLinks.add(Tools.getPathAsString(substitutePath));
        return substituteLink;
      }
    }

    return nominalLink;
  }
  public static void generateInstanceStreamWithGlobalMatrix(
      IProductManagerLocal productService,
      List<PartLink> currentPath,
      Matrix4d matrix,
      VirtualInstanceCollection virtualInstanceCollection,
      List<Integer> instanceIds,
      JsonGenerator jg) {
    try {

      PartLink partLink = currentPath.get(currentPath.size() - 1);
      PSFilter filter = virtualInstanceCollection.getFilter();
      List<PartIteration> filteredPartIterations = filter.filter(partLink.getComponent());

      if (!filteredPartIterations.isEmpty()) {

        PartIteration partI = filteredPartIterations.iterator().next();

        // Filter ACL on part
        if (!productService.canAccess(partI.getPartRevision().getKey())) {
          return;
        }

        for (CADInstance instance : partLink.getCadInstances()) {

          List<Integer> copyInstanceIds = new ArrayList<>(instanceIds);
          copyInstanceIds.add(instance.getId());

          Vector3d instanceTranslation =
              new Vector3d(instance.getTx(), instance.getTy(), instance.getTz());
          Vector3d instanceRotation =
              new Vector3d(instance.getRx(), instance.getRy(), instance.getRz());
          Matrix4d combinedMatrix =
              combineTransformation(matrix, instanceTranslation, instanceRotation);

          if (!partI.isAssembly() && !partI.getGeometries().isEmpty()) {
            writeLeaf(currentPath, copyInstanceIds, partI, combinedMatrix, jg);
          } else {
            for (PartLink subLink : partI.getComponents()) {
              List<PartLink> subPath = new ArrayList<>(currentPath);
              subPath.add(subLink);
              generateInstanceStreamWithGlobalMatrix(
                  productService,
                  subPath,
                  combinedMatrix,
                  virtualInstanceCollection,
                  copyInstanceIds,
                  jg);
            }
          }
        }
      }

    } catch (UserNotFoundException
        | UserNotActiveException
        | WorkspaceNotFoundException
        | PartRevisionNotFoundException e) {
      e.printStackTrace();
    }
  }
  public static void generateInstanceStreamWithGlobalMatrix(
      IProductManagerLocal productService,
      List<PartLink> currentPath,
      Matrix4d matrix,
      InstanceCollection instanceCollection,
      List<Integer> instanceIds,
      JsonGenerator jg) {

    try {

      if (currentPath == null) {
        PartLink rootPartUsageLink =
            productService.getRootPartUsageLink(instanceCollection.getCiKey());
        currentPath = new ArrayList<>();
        currentPath.add(rootPartUsageLink);
      }

      Component component =
          productService.filterProductStructure(
              instanceCollection.getCiKey(), instanceCollection.getFilter(), currentPath, 1);

      PartLink partLink = component.getPartLink();
      PartIteration partI = component.getRetainedIteration();

      // Filter ACL on part
      if (!productService.canAccess(partI.getPartRevision().getKey())) {
        return;
      }

      for (CADInstance instance : partLink.getCadInstances()) {

        List<Integer> copyInstanceIds = new ArrayList<>(instanceIds);
        copyInstanceIds.add(instance.getId());

        Vector3d instanceTranslation =
            new Vector3d(instance.getTx(), instance.getTy(), instance.getTz());
        Vector3d instanceRotation =
            new Vector3d(instance.getRx(), instance.getRy(), instance.getRz());
        Matrix4d combinedMatrix =
            combineTransformation(matrix, instanceTranslation, instanceRotation);

        if (!partI.isAssembly()
            && !partI.getGeometries().isEmpty()
            && instanceCollection.isFiltered(currentPath)) {
          writeLeaf(currentPath, copyInstanceIds, partI, combinedMatrix, jg);
        } else {
          for (Component subComponent : component.getComponents()) {
            generateInstanceStreamWithGlobalMatrix(
                productService,
                subComponent.getPath(),
                combinedMatrix,
                instanceCollection,
                copyInstanceIds,
                jg);
          }
        }
      }

    } catch (PartMasterNotFoundException
        | PartRevisionNotFoundException
        | PartUsageLinkNotFoundException
        | UserNotFoundException
        | WorkspaceNotFoundException
        | ConfigurationItemNotFoundException e) {
      LOGGER.log(Level.SEVERE, null, e);
    } catch (AccessRightException
        | EntityConstraintException
        | NotAllowedException
        | UserNotActiveException e) {
      LOGGER.log(Level.FINEST, null, e);
    }
  }