private static <E extends PackagingElement<?>> boolean processElementsWithSubstitutions(
      @NotNull List<? extends PackagingElement<?>> elements,
      @NotNull PackagingElementResolvingContext context,
      @NotNull ArtifactType artifactType,
      @NotNull PackagingElementPath parentPath,
      @NotNull PackagingElementProcessor<E> processor,
      final Set<PackagingElement<?>> processed) {
    for (PackagingElement<?> element : elements) {
      if (!processed.add(element)) {
        continue;
      }

      if (element instanceof ComplexPackagingElement<?>
          && processor.shouldProcessSubstitution((ComplexPackagingElement) element)) {
        final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>) element;
        final List<? extends PackagingElement<?>> substitution =
            complexElement.getSubstitution(context, artifactType);
        if (substitution != null
            && !processElementsWithSubstitutions(
                substitution,
                context,
                artifactType,
                parentPath.appendComplex(complexElement),
                processor,
                processed)) {
          return false;
        }
      } else if (!processor.process((E) element, parentPath)) {
        return false;
      }
    }
    return true;
  }
 private static <E extends PackagingElement<?>> boolean processElementRecursively(
     @NotNull PackagingElement<?> element,
     @Nullable PackagingElementType<E> type,
     @NotNull PackagingElementProcessor<? super E> processor,
     @NotNull PackagingElementResolvingContext resolvingContext,
     final boolean processSubstitutions,
     ArtifactType artifactType,
     @NotNull PackagingElementPath path,
     Set<PackagingElement<?>> processed) {
   if (!processor.shouldProcess(element) || !processed.add(element)) {
     return true;
   }
   if (type == null || element.getType().equals(type)) {
     if (!processor.process((E) element, path)) {
       return false;
     }
   }
   if (element instanceof CompositePackagingElement<?>) {
     final CompositePackagingElement<?> composite = (CompositePackagingElement<?>) element;
     return processElementsRecursively(
         composite.getChildren(),
         type,
         processor,
         resolvingContext,
         processSubstitutions,
         artifactType,
         path.appendComposite(composite),
         processed);
   } else if (element instanceof ComplexPackagingElement<?> && processSubstitutions) {
     final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>) element;
     if (processor.shouldProcessSubstitution(complexElement)) {
       final List<? extends PackagingElement<?>> substitution =
           complexElement.getSubstitution(resolvingContext, artifactType);
       if (substitution != null) {
         return processElementsRecursively(
             substitution,
             type,
             processor,
             resolvingContext,
             processSubstitutions,
             artifactType,
             path.appendComplex(complexElement),
             processed);
       }
     }
   }
   return true;
 }