@Test public void testBuildWhenOutputsMapToAllOtherInputs() { final List<TestProcessor> processors = Arrays.asList(NeedsMapProducesMap, NeedsTableProducesTable); try { this.processorDependencyGraphFactory.build(processors, Collections.emptyMap()); fail("A prossessor shouldn't be able to depends on himself"); } catch (IllegalArgumentException e) { } }
@SuppressWarnings("unchecked") public ProcessorDependencyGraph build( final List<? extends Processor> processors, final Map<String, Class<?>> attributes) { ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); final Map<String, ProcessorGraphNode<Object, Object>> provideByProcessor = new HashMap<>(); final Map<String, Class<?>> outputTypes = new HashMap<>(attributes); outputTypes.put(Values.VALUES_KEY, Values.class); outputTypes.put(Values.TASK_DIRECTORY_KEY, File.class); outputTypes.put(Values.CLIENT_HTTP_REQUEST_FACTORY_KEY, MfClientHttpRequestFactoryProvider.class); outputTypes.put(Values.TEMPLATE_KEY, Template.class); outputTypes.put(Values.PDF_CONFIG_KEY, PDFConfig.class); outputTypes.put(Values.SUBREPORT_DIR_KEY, String.class); outputTypes.put(Values.OUTPUT_FORMAT_KEY, String.class); outputTypes.put(Values.JOB_ID_KEY, String.class); outputTypes.put(MapPrinterServlet.JSON_REQUEST_HEADERS, HttpRequestHeadersAttribute.Value.class); outputTypes.put(Values.LOCALE_KEY, Locale.class); for (Processor processor: processors) { final ProcessorGraphNode<Object, Object> node = new ProcessorGraphNode<Object, Object>(processor, this.metricRegistry); final Set<InputValue> inputs = getInputs(node.getProcessor()); boolean isRoot = true; for (InputValue input: inputs) { if (input.name.equals(Values.VALUES_KEY)) { if (processor instanceof CustomDependencies) { for (String name: ((CustomDependencies) processor).getDependencies()) { final Class<?> outputType = outputTypes.get(name); if (outputType == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the dynamic input '%s'.", processor, name)); } final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(name); if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } } else { for (ProcessorGraphNode<Object, Object> processorSolution: provideByProcessor.values()) { processorSolution.addDependency(node); isRoot = false; } } } else { final Class<?> outputType = outputTypes.get(input.name); if (outputType != null) { final Class<?> inputType = input.type; final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(input.name); if (inputType.isAssignableFrom(outputType)) { if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } else { if (processorSolution != null) { throw new IllegalArgumentException(String.format( "Type conflict: Processor '%s' provides an output with name '%s' " + "and of type '%s', while processor '%s' expects an input " + "of that name with type '%s'! Please rename one of the " + "attributes in the mappings of the processors.", processorSolution.getName(), input.name, outputType, node.getName(), inputType)); } else { throw new IllegalArgumentException(String.format( "Type conflict: the attribute '%s' of type '%s', while processor " + "'%s' expects an input of that name with type '%s'!", input.name, outputType, node.getName(), inputType)); } } } else { if (input.field.getAnnotation(HasDefaultValue.class) == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the input '%s'.", processor, input.name)); } } } } if (isRoot) { graph.addRoot(node); } for (OutputValue value: getOutputValues(node.getProcessor())) { String outputName = value.name; if (outputTypes.containsKey(outputName)) { if (value.canBeRenamed) { outputName = outputName + "_" + UUID.randomUUID().toString(); } else { ProcessorGraphNode<Object, Object> provider = provideByProcessor.get(outputName); if (provider == null) { throw new IllegalArgumentException(String.format( "Processors '%s' provide the output '%s' who is already declared as " + "an attribute. You have to rename one of the outputs and the " + "corresponding input so that there is no ambiguity with " + "regards to the input a processor consumes.", processor, outputName)); } else { throw new IllegalArgumentException(String.format( "Multiple processors provide the same output mapping: '%s' and " + "'%s' both provide: '%s'. You have to rename one of the " + "outputs and the corresponding input so that there is no " + "ambiguity with regards to the input a processor consumes.", processor, provider, outputName)); } } } provideByProcessor.put(outputName, node); outputTypes.put(outputName, value.type); } for (InputValue input: inputs) { if (input.field.getAnnotation(InputOutputValue.class) != null) { provideByProcessor.put(input.name, node); } } } final Collection<? extends Processor> missingProcessors = CollectionUtils.subtract( processors, graph.getAllProcessors()); final StringBuilder missingProcessorsName = new StringBuilder(); for (Processor p: missingProcessors) { missingProcessorsName.append("\n- "); missingProcessorsName.append(p.toString()); } Assert.isTrue( missingProcessors.isEmpty(), "The processor graph:\n" + graph + "\ndoes not contain all the processors, missing:" + missingProcessorsName); return graph; }
ProcessorDependencyGraphFactory { @SuppressWarnings("unchecked") public ProcessorDependencyGraph build( final List<? extends Processor> processors, final Map<String, Class<?>> attributes) { ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); final Map<String, ProcessorGraphNode<Object, Object>> provideByProcessor = new HashMap<>(); final Map<String, Class<?>> outputTypes = new HashMap<>(attributes); outputTypes.put(Values.VALUES_KEY, Values.class); outputTypes.put(Values.TASK_DIRECTORY_KEY, File.class); outputTypes.put(Values.CLIENT_HTTP_REQUEST_FACTORY_KEY, MfClientHttpRequestFactoryProvider.class); outputTypes.put(Values.TEMPLATE_KEY, Template.class); outputTypes.put(Values.PDF_CONFIG_KEY, PDFConfig.class); outputTypes.put(Values.SUBREPORT_DIR_KEY, String.class); outputTypes.put(Values.OUTPUT_FORMAT_KEY, String.class); outputTypes.put(Values.JOB_ID_KEY, String.class); outputTypes.put(MapPrinterServlet.JSON_REQUEST_HEADERS, HttpRequestHeadersAttribute.Value.class); outputTypes.put(Values.LOCALE_KEY, Locale.class); for (Processor processor: processors) { final ProcessorGraphNode<Object, Object> node = new ProcessorGraphNode<Object, Object>(processor, this.metricRegistry); final Set<InputValue> inputs = getInputs(node.getProcessor()); boolean isRoot = true; for (InputValue input: inputs) { if (input.name.equals(Values.VALUES_KEY)) { if (processor instanceof CustomDependencies) { for (String name: ((CustomDependencies) processor).getDependencies()) { final Class<?> outputType = outputTypes.get(name); if (outputType == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the dynamic input '%s'.", processor, name)); } final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(name); if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } } else { for (ProcessorGraphNode<Object, Object> processorSolution: provideByProcessor.values()) { processorSolution.addDependency(node); isRoot = false; } } } else { final Class<?> outputType = outputTypes.get(input.name); if (outputType != null) { final Class<?> inputType = input.type; final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(input.name); if (inputType.isAssignableFrom(outputType)) { if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } else { if (processorSolution != null) { throw new IllegalArgumentException(String.format( "Type conflict: Processor '%s' provides an output with name '%s' " + "and of type '%s', while processor '%s' expects an input " + "of that name with type '%s'! Please rename one of the " + "attributes in the mappings of the processors.", processorSolution.getName(), input.name, outputType, node.getName(), inputType)); } else { throw new IllegalArgumentException(String.format( "Type conflict: the attribute '%s' of type '%s', while processor " + "'%s' expects an input of that name with type '%s'!", input.name, outputType, node.getName(), inputType)); } } } else { if (input.field.getAnnotation(HasDefaultValue.class) == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the input '%s'.", processor, input.name)); } } } } if (isRoot) { graph.addRoot(node); } for (OutputValue value: getOutputValues(node.getProcessor())) { String outputName = value.name; if (outputTypes.containsKey(outputName)) { if (value.canBeRenamed) { outputName = outputName + "_" + UUID.randomUUID().toString(); } else { ProcessorGraphNode<Object, Object> provider = provideByProcessor.get(outputName); if (provider == null) { throw new IllegalArgumentException(String.format( "Processors '%s' provide the output '%s' who is already declared as " + "an attribute. You have to rename one of the outputs and the " + "corresponding input so that there is no ambiguity with " + "regards to the input a processor consumes.", processor, outputName)); } else { throw new IllegalArgumentException(String.format( "Multiple processors provide the same output mapping: '%s' and " + "'%s' both provide: '%s'. You have to rename one of the " + "outputs and the corresponding input so that there is no " + "ambiguity with regards to the input a processor consumes.", processor, provider, outputName)); } } } provideByProcessor.put(outputName, node); outputTypes.put(outputName, value.type); } for (InputValue input: inputs) { if (input.field.getAnnotation(InputOutputValue.class) != null) { provideByProcessor.put(input.name, node); } } } final Collection<? extends Processor> missingProcessors = CollectionUtils.subtract( processors, graph.getAllProcessors()); final StringBuilder missingProcessorsName = new StringBuilder(); for (Processor p: missingProcessors) { missingProcessorsName.append("\n- "); missingProcessorsName.append(p.toString()); } Assert.isTrue( missingProcessors.isEmpty(), "The processor graph:\n" + graph + "\ndoes not contain all the processors, missing:" + missingProcessorsName); return graph; } }
ProcessorDependencyGraphFactory { @SuppressWarnings("unchecked") public ProcessorDependencyGraph build( final List<? extends Processor> processors, final Map<String, Class<?>> attributes) { ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); final Map<String, ProcessorGraphNode<Object, Object>> provideByProcessor = new HashMap<>(); final Map<String, Class<?>> outputTypes = new HashMap<>(attributes); outputTypes.put(Values.VALUES_KEY, Values.class); outputTypes.put(Values.TASK_DIRECTORY_KEY, File.class); outputTypes.put(Values.CLIENT_HTTP_REQUEST_FACTORY_KEY, MfClientHttpRequestFactoryProvider.class); outputTypes.put(Values.TEMPLATE_KEY, Template.class); outputTypes.put(Values.PDF_CONFIG_KEY, PDFConfig.class); outputTypes.put(Values.SUBREPORT_DIR_KEY, String.class); outputTypes.put(Values.OUTPUT_FORMAT_KEY, String.class); outputTypes.put(Values.JOB_ID_KEY, String.class); outputTypes.put(MapPrinterServlet.JSON_REQUEST_HEADERS, HttpRequestHeadersAttribute.Value.class); outputTypes.put(Values.LOCALE_KEY, Locale.class); for (Processor processor: processors) { final ProcessorGraphNode<Object, Object> node = new ProcessorGraphNode<Object, Object>(processor, this.metricRegistry); final Set<InputValue> inputs = getInputs(node.getProcessor()); boolean isRoot = true; for (InputValue input: inputs) { if (input.name.equals(Values.VALUES_KEY)) { if (processor instanceof CustomDependencies) { for (String name: ((CustomDependencies) processor).getDependencies()) { final Class<?> outputType = outputTypes.get(name); if (outputType == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the dynamic input '%s'.", processor, name)); } final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(name); if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } } else { for (ProcessorGraphNode<Object, Object> processorSolution: provideByProcessor.values()) { processorSolution.addDependency(node); isRoot = false; } } } else { final Class<?> outputType = outputTypes.get(input.name); if (outputType != null) { final Class<?> inputType = input.type; final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(input.name); if (inputType.isAssignableFrom(outputType)) { if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } else { if (processorSolution != null) { throw new IllegalArgumentException(String.format( "Type conflict: Processor '%s' provides an output with name '%s' " + "and of type '%s', while processor '%s' expects an input " + "of that name with type '%s'! Please rename one of the " + "attributes in the mappings of the processors.", processorSolution.getName(), input.name, outputType, node.getName(), inputType)); } else { throw new IllegalArgumentException(String.format( "Type conflict: the attribute '%s' of type '%s', while processor " + "'%s' expects an input of that name with type '%s'!", input.name, outputType, node.getName(), inputType)); } } } else { if (input.field.getAnnotation(HasDefaultValue.class) == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the input '%s'.", processor, input.name)); } } } } if (isRoot) { graph.addRoot(node); } for (OutputValue value: getOutputValues(node.getProcessor())) { String outputName = value.name; if (outputTypes.containsKey(outputName)) { if (value.canBeRenamed) { outputName = outputName + "_" + UUID.randomUUID().toString(); } else { ProcessorGraphNode<Object, Object> provider = provideByProcessor.get(outputName); if (provider == null) { throw new IllegalArgumentException(String.format( "Processors '%s' provide the output '%s' who is already declared as " + "an attribute. You have to rename one of the outputs and the " + "corresponding input so that there is no ambiguity with " + "regards to the input a processor consumes.", processor, outputName)); } else { throw new IllegalArgumentException(String.format( "Multiple processors provide the same output mapping: '%s' and " + "'%s' both provide: '%s'. You have to rename one of the " + "outputs and the corresponding input so that there is no " + "ambiguity with regards to the input a processor consumes.", processor, provider, outputName)); } } } provideByProcessor.put(outputName, node); outputTypes.put(outputName, value.type); } for (InputValue input: inputs) { if (input.field.getAnnotation(InputOutputValue.class) != null) { provideByProcessor.put(input.name, node); } } } final Collection<? extends Processor> missingProcessors = CollectionUtils.subtract( processors, graph.getAllProcessors()); final StringBuilder missingProcessorsName = new StringBuilder(); for (Processor p: missingProcessors) { missingProcessorsName.append("\n- "); missingProcessorsName.append(p.toString()); } Assert.isTrue( missingProcessors.isEmpty(), "The processor graph:\n" + graph + "\ndoes not contain all the processors, missing:" + missingProcessorsName); return graph; }  }
ProcessorDependencyGraphFactory { @SuppressWarnings("unchecked") public ProcessorDependencyGraph build( final List<? extends Processor> processors, final Map<String, Class<?>> attributes) { ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); final Map<String, ProcessorGraphNode<Object, Object>> provideByProcessor = new HashMap<>(); final Map<String, Class<?>> outputTypes = new HashMap<>(attributes); outputTypes.put(Values.VALUES_KEY, Values.class); outputTypes.put(Values.TASK_DIRECTORY_KEY, File.class); outputTypes.put(Values.CLIENT_HTTP_REQUEST_FACTORY_KEY, MfClientHttpRequestFactoryProvider.class); outputTypes.put(Values.TEMPLATE_KEY, Template.class); outputTypes.put(Values.PDF_CONFIG_KEY, PDFConfig.class); outputTypes.put(Values.SUBREPORT_DIR_KEY, String.class); outputTypes.put(Values.OUTPUT_FORMAT_KEY, String.class); outputTypes.put(Values.JOB_ID_KEY, String.class); outputTypes.put(MapPrinterServlet.JSON_REQUEST_HEADERS, HttpRequestHeadersAttribute.Value.class); outputTypes.put(Values.LOCALE_KEY, Locale.class); for (Processor processor: processors) { final ProcessorGraphNode<Object, Object> node = new ProcessorGraphNode<Object, Object>(processor, this.metricRegistry); final Set<InputValue> inputs = getInputs(node.getProcessor()); boolean isRoot = true; for (InputValue input: inputs) { if (input.name.equals(Values.VALUES_KEY)) { if (processor instanceof CustomDependencies) { for (String name: ((CustomDependencies) processor).getDependencies()) { final Class<?> outputType = outputTypes.get(name); if (outputType == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the dynamic input '%s'.", processor, name)); } final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(name); if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } } else { for (ProcessorGraphNode<Object, Object> processorSolution: provideByProcessor.values()) { processorSolution.addDependency(node); isRoot = false; } } } else { final Class<?> outputType = outputTypes.get(input.name); if (outputType != null) { final Class<?> inputType = input.type; final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(input.name); if (inputType.isAssignableFrom(outputType)) { if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } else { if (processorSolution != null) { throw new IllegalArgumentException(String.format( "Type conflict: Processor '%s' provides an output with name '%s' " + "and of type '%s', while processor '%s' expects an input " + "of that name with type '%s'! Please rename one of the " + "attributes in the mappings of the processors.", processorSolution.getName(), input.name, outputType, node.getName(), inputType)); } else { throw new IllegalArgumentException(String.format( "Type conflict: the attribute '%s' of type '%s', while processor " + "'%s' expects an input of that name with type '%s'!", input.name, outputType, node.getName(), inputType)); } } } else { if (input.field.getAnnotation(HasDefaultValue.class) == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the input '%s'.", processor, input.name)); } } } } if (isRoot) { graph.addRoot(node); } for (OutputValue value: getOutputValues(node.getProcessor())) { String outputName = value.name; if (outputTypes.containsKey(outputName)) { if (value.canBeRenamed) { outputName = outputName + "_" + UUID.randomUUID().toString(); } else { ProcessorGraphNode<Object, Object> provider = provideByProcessor.get(outputName); if (provider == null) { throw new IllegalArgumentException(String.format( "Processors '%s' provide the output '%s' who is already declared as " + "an attribute. You have to rename one of the outputs and the " + "corresponding input so that there is no ambiguity with " + "regards to the input a processor consumes.", processor, outputName)); } else { throw new IllegalArgumentException(String.format( "Multiple processors provide the same output mapping: '%s' and " + "'%s' both provide: '%s'. You have to rename one of the " + "outputs and the corresponding input so that there is no " + "ambiguity with regards to the input a processor consumes.", processor, provider, outputName)); } } } provideByProcessor.put(outputName, node); outputTypes.put(outputName, value.type); } for (InputValue input: inputs) { if (input.field.getAnnotation(InputOutputValue.class) != null) { provideByProcessor.put(input.name, node); } } } final Collection<? extends Processor> missingProcessors = CollectionUtils.subtract( processors, graph.getAllProcessors()); final StringBuilder missingProcessorsName = new StringBuilder(); for (Processor p: missingProcessors) { missingProcessorsName.append("\n- "); missingProcessorsName.append(p.toString()); } Assert.isTrue( missingProcessors.isEmpty(), "The processor graph:\n" + graph + "\ndoes not contain all the processors, missing:" + missingProcessorsName); return graph; }  static void fillProcessorAttributes(
            final List<Processor> processors,
            final Map<String, Attribute> initialAttributes); @SuppressWarnings("unchecked") ProcessorDependencyGraph build(
            final List<? extends Processor> processors,
            final Map<String, Class<?>> attributes); }
ProcessorDependencyGraphFactory { @SuppressWarnings("unchecked") public ProcessorDependencyGraph build( final List<? extends Processor> processors, final Map<String, Class<?>> attributes) { ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); final Map<String, ProcessorGraphNode<Object, Object>> provideByProcessor = new HashMap<>(); final Map<String, Class<?>> outputTypes = new HashMap<>(attributes); outputTypes.put(Values.VALUES_KEY, Values.class); outputTypes.put(Values.TASK_DIRECTORY_KEY, File.class); outputTypes.put(Values.CLIENT_HTTP_REQUEST_FACTORY_KEY, MfClientHttpRequestFactoryProvider.class); outputTypes.put(Values.TEMPLATE_KEY, Template.class); outputTypes.put(Values.PDF_CONFIG_KEY, PDFConfig.class); outputTypes.put(Values.SUBREPORT_DIR_KEY, String.class); outputTypes.put(Values.OUTPUT_FORMAT_KEY, String.class); outputTypes.put(Values.JOB_ID_KEY, String.class); outputTypes.put(MapPrinterServlet.JSON_REQUEST_HEADERS, HttpRequestHeadersAttribute.Value.class); outputTypes.put(Values.LOCALE_KEY, Locale.class); for (Processor processor: processors) { final ProcessorGraphNode<Object, Object> node = new ProcessorGraphNode<Object, Object>(processor, this.metricRegistry); final Set<InputValue> inputs = getInputs(node.getProcessor()); boolean isRoot = true; for (InputValue input: inputs) { if (input.name.equals(Values.VALUES_KEY)) { if (processor instanceof CustomDependencies) { for (String name: ((CustomDependencies) processor).getDependencies()) { final Class<?> outputType = outputTypes.get(name); if (outputType == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the dynamic input '%s'.", processor, name)); } final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(name); if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } } else { for (ProcessorGraphNode<Object, Object> processorSolution: provideByProcessor.values()) { processorSolution.addDependency(node); isRoot = false; } } } else { final Class<?> outputType = outputTypes.get(input.name); if (outputType != null) { final Class<?> inputType = input.type; final ProcessorGraphNode<Object, Object> processorSolution = provideByProcessor.get(input.name); if (inputType.isAssignableFrom(outputType)) { if (processorSolution != null) { processorSolution.addDependency(node); isRoot = false; } } else { if (processorSolution != null) { throw new IllegalArgumentException(String.format( "Type conflict: Processor '%s' provides an output with name '%s' " + "and of type '%s', while processor '%s' expects an input " + "of that name with type '%s'! Please rename one of the " + "attributes in the mappings of the processors.", processorSolution.getName(), input.name, outputType, node.getName(), inputType)); } else { throw new IllegalArgumentException(String.format( "Type conflict: the attribute '%s' of type '%s', while processor " + "'%s' expects an input of that name with type '%s'!", input.name, outputType, node.getName(), inputType)); } } } else { if (input.field.getAnnotation(HasDefaultValue.class) == null) { throw new IllegalArgumentException(String.format( "The Processor '%s' has no value for the input '%s'.", processor, input.name)); } } } } if (isRoot) { graph.addRoot(node); } for (OutputValue value: getOutputValues(node.getProcessor())) { String outputName = value.name; if (outputTypes.containsKey(outputName)) { if (value.canBeRenamed) { outputName = outputName + "_" + UUID.randomUUID().toString(); } else { ProcessorGraphNode<Object, Object> provider = provideByProcessor.get(outputName); if (provider == null) { throw new IllegalArgumentException(String.format( "Processors '%s' provide the output '%s' who is already declared as " + "an attribute. You have to rename one of the outputs and the " + "corresponding input so that there is no ambiguity with " + "regards to the input a processor consumes.", processor, outputName)); } else { throw new IllegalArgumentException(String.format( "Multiple processors provide the same output mapping: '%s' and " + "'%s' both provide: '%s'. You have to rename one of the " + "outputs and the corresponding input so that there is no " + "ambiguity with regards to the input a processor consumes.", processor, provider, outputName)); } } } provideByProcessor.put(outputName, node); outputTypes.put(outputName, value.type); } for (InputValue input: inputs) { if (input.field.getAnnotation(InputOutputValue.class) != null) { provideByProcessor.put(input.name, node); } } } final Collection<? extends Processor> missingProcessors = CollectionUtils.subtract( processors, graph.getAllProcessors()); final StringBuilder missingProcessorsName = new StringBuilder(); for (Processor p: missingProcessors) { missingProcessorsName.append("\n- "); missingProcessorsName.append(p.toString()); } Assert.isTrue( missingProcessors.isEmpty(), "The processor graph:\n" + graph + "\ndoes not contain all the processors, missing:" + missingProcessorsName); return graph; }  static void fillProcessorAttributes(
            final List<Processor> processors,
            final Map<String, Attribute> initialAttributes); @SuppressWarnings("unchecked") ProcessorDependencyGraph build(
            final List<? extends Processor> processors,
            final Map<String, Class<?>> attributes);  }