@Test void customConverter() throws Exception { final List<SimplePropertyDefinition> properties = singletonList(new SimplePropertyDefinition("entry", "entry", "Entry", "String", null, NVAL, emptyMap(), null, NPROPS)); final PropertyContext<Object> propertyContext = new PropertyContext<>(properties.iterator().next(), null, new PropertyContext.Configuration(false)); final List<UiSchema> schemas = new ArrayList<>(); try (final Jsonb jsonb = JsonbBuilder.create()) { final JsonSchema jsonSchema = new JsonSchema(); new JsonSchemaConverter(jsonb, jsonSchema, properties) .convert(completedFuture(propertyContext)) .toCompletableFuture() .get(); new UiSchemaConverter(null, "test", schemas, properties, null, jsonSchema, properties, emptyList(), "en", singletonList(new CustomPropertyConverter() { @Override public boolean supports(final PropertyContext<?> context) { return context.getProperty().getName().equals("entry"); } @Override public CompletionStage<PropertyContext<?>> convert( final CompletionStage<PropertyContext<?>> context, final ConverterContext converterContext) { return new DataListWidgetConverter(converterContext.getSchemas(), converterContext.getProperties(), converterContext.getActions(), converterContext.getClient(), converterContext.getFamily(), converterContext.getJsonSchema(), converterContext.getLang()) { @Override protected CompletionStage<PropertyContext<?>> fillProposalsAndReturn( final PropertyContext<?> context, final UiSchema schema, final JsonSchema jsonSchema) { schema .setTitleMap(singletonList(new UiSchema.NameValue.Builder() .withName("a") .withValue("A") .build())); jsonSchema.setEnumValues(singletonList("A")); return completedFuture(context); } }.convert(context); } }), new AtomicInteger(1)).convert(completedFuture(propertyContext)).toCompletableFuture().get(); } assertEquals(1, schemas.size()); final UiSchema entrySchema = schemas.iterator().next(); assertEquals("datalist", entrySchema.getWidget()); assertEquals(1, entrySchema.getTitleMap().size()); final UiSchema.NameValue proposal = entrySchema.getTitleMap().iterator().next(); assertEquals("a", proposal.getName()); assertEquals("A", proposal.getValue()); }
@Override public CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs) { return cs .thenCompose(context -> customConverters .stream() .filter(it -> it.supports(context)) .findFirst() .map(it -> it .convert(cs, new CustomPropertyConverter.ConverterContext(family, schemas, includedProperties, client, jsonSchema, properties, actions, lang))) .orElseGet(() -> { final SimplePropertyDefinition property = context.getProperty(); final String type = property.getType().toLowerCase(Locale.ROOT); final Map<String, String> metadata = property.getMetadata(); switch (type) { case "object": return convertObject(context, metadata, null, idGenerator); case "boolean": includedProperties.add(property); return new ToggleWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "enum": includedProperties.add(property); return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "number": includedProperties.add(property); return new NumberWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "array": includedProperties.add(property); final String nestedPrefix = property.getPath() + "[]."; final int from = nestedPrefix.length(); final Collection<SimplePropertyDefinition> nested = properties .stream() .filter(prop -> prop.getPath().startsWith(nestedPrefix) && prop.getPath().indexOf('.', from) < 0) .collect(toList()); if (!nested.isEmpty()) { return new ObjectArrayWidgetConverter(schemas, properties, actions, family, client, gridLayoutFilter, jsonSchema, lang, customConverters, metadata, idGenerator) .convert(CompletableFuture.completedFuture(context)); } return new MultiSelectWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "string": default: if (property.getPath().endsWith("[]")) { return CompletableFuture.completedFuture(context); } includedProperties.add(property); if ("true".equalsIgnoreCase(metadata.get("ui::credential"))) { return new CredentialWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::code::value")) { return new CodeWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::suggestions") || metadata.containsKey("action::built_in_suggestable")) { return new SuggestionWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::dynamic_values")) { return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::textarea") && Boolean.valueOf(metadata.get("ui::textarea"))) { return new TextAreaWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::datetime")) { return new DateTimeConverter(schemas, properties, actions, jsonSchema, lang, metadata.get("ui::datetime")) .convert(CompletableFuture.completedFuture(context)); } return new TextWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } })); }
UiSchemaConverter implements PropertyConverter { @Override public CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs) { return cs .thenCompose(context -> customConverters .stream() .filter(it -> it.supports(context)) .findFirst() .map(it -> it .convert(cs, new CustomPropertyConverter.ConverterContext(family, schemas, includedProperties, client, jsonSchema, properties, actions, lang))) .orElseGet(() -> { final SimplePropertyDefinition property = context.getProperty(); final String type = property.getType().toLowerCase(Locale.ROOT); final Map<String, String> metadata = property.getMetadata(); switch (type) { case "object": return convertObject(context, metadata, null, idGenerator); case "boolean": includedProperties.add(property); return new ToggleWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "enum": includedProperties.add(property); return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "number": includedProperties.add(property); return new NumberWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "array": includedProperties.add(property); final String nestedPrefix = property.getPath() + "[]."; final int from = nestedPrefix.length(); final Collection<SimplePropertyDefinition> nested = properties .stream() .filter(prop -> prop.getPath().startsWith(nestedPrefix) && prop.getPath().indexOf('.', from) < 0) .collect(toList()); if (!nested.isEmpty()) { return new ObjectArrayWidgetConverter(schemas, properties, actions, family, client, gridLayoutFilter, jsonSchema, lang, customConverters, metadata, idGenerator) .convert(CompletableFuture.completedFuture(context)); } return new MultiSelectWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "string": default: if (property.getPath().endsWith("[]")) { return CompletableFuture.completedFuture(context); } includedProperties.add(property); if ("true".equalsIgnoreCase(metadata.get("ui::credential"))) { return new CredentialWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::code::value")) { return new CodeWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::suggestions") || metadata.containsKey("action::built_in_suggestable")) { return new SuggestionWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::dynamic_values")) { return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::textarea") && Boolean.valueOf(metadata.get("ui::textarea"))) { return new TextAreaWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::datetime")) { return new DateTimeConverter(schemas, properties, actions, jsonSchema, lang, metadata.get("ui::datetime")) .convert(CompletableFuture.completedFuture(context)); } return new TextWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } })); } }
UiSchemaConverter implements PropertyConverter { @Override public CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs) { return cs .thenCompose(context -> customConverters .stream() .filter(it -> it.supports(context)) .findFirst() .map(it -> it .convert(cs, new CustomPropertyConverter.ConverterContext(family, schemas, includedProperties, client, jsonSchema, properties, actions, lang))) .orElseGet(() -> { final SimplePropertyDefinition property = context.getProperty(); final String type = property.getType().toLowerCase(Locale.ROOT); final Map<String, String> metadata = property.getMetadata(); switch (type) { case "object": return convertObject(context, metadata, null, idGenerator); case "boolean": includedProperties.add(property); return new ToggleWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "enum": includedProperties.add(property); return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "number": includedProperties.add(property); return new NumberWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "array": includedProperties.add(property); final String nestedPrefix = property.getPath() + "[]."; final int from = nestedPrefix.length(); final Collection<SimplePropertyDefinition> nested = properties .stream() .filter(prop -> prop.getPath().startsWith(nestedPrefix) && prop.getPath().indexOf('.', from) < 0) .collect(toList()); if (!nested.isEmpty()) { return new ObjectArrayWidgetConverter(schemas, properties, actions, family, client, gridLayoutFilter, jsonSchema, lang, customConverters, metadata, idGenerator) .convert(CompletableFuture.completedFuture(context)); } return new MultiSelectWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "string": default: if (property.getPath().endsWith("[]")) { return CompletableFuture.completedFuture(context); } includedProperties.add(property); if ("true".equalsIgnoreCase(metadata.get("ui::credential"))) { return new CredentialWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::code::value")) { return new CodeWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::suggestions") || metadata.containsKey("action::built_in_suggestable")) { return new SuggestionWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::dynamic_values")) { return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::textarea") && Boolean.valueOf(metadata.get("ui::textarea"))) { return new TextAreaWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::datetime")) { return new DateTimeConverter(schemas, properties, actions, jsonSchema, lang, metadata.get("ui::datetime")) .convert(CompletableFuture.completedFuture(context)); } return new TextWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } })); }  }
UiSchemaConverter implements PropertyConverter { @Override public CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs) { return cs .thenCompose(context -> customConverters .stream() .filter(it -> it.supports(context)) .findFirst() .map(it -> it .convert(cs, new CustomPropertyConverter.ConverterContext(family, schemas, includedProperties, client, jsonSchema, properties, actions, lang))) .orElseGet(() -> { final SimplePropertyDefinition property = context.getProperty(); final String type = property.getType().toLowerCase(Locale.ROOT); final Map<String, String> metadata = property.getMetadata(); switch (type) { case "object": return convertObject(context, metadata, null, idGenerator); case "boolean": includedProperties.add(property); return new ToggleWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "enum": includedProperties.add(property); return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "number": includedProperties.add(property); return new NumberWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "array": includedProperties.add(property); final String nestedPrefix = property.getPath() + "[]."; final int from = nestedPrefix.length(); final Collection<SimplePropertyDefinition> nested = properties .stream() .filter(prop -> prop.getPath().startsWith(nestedPrefix) && prop.getPath().indexOf('.', from) < 0) .collect(toList()); if (!nested.isEmpty()) { return new ObjectArrayWidgetConverter(schemas, properties, actions, family, client, gridLayoutFilter, jsonSchema, lang, customConverters, metadata, idGenerator) .convert(CompletableFuture.completedFuture(context)); } return new MultiSelectWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "string": default: if (property.getPath().endsWith("[]")) { return CompletableFuture.completedFuture(context); } includedProperties.add(property); if ("true".equalsIgnoreCase(metadata.get("ui::credential"))) { return new CredentialWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::code::value")) { return new CodeWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::suggestions") || metadata.containsKey("action::built_in_suggestable")) { return new SuggestionWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::dynamic_values")) { return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::textarea") && Boolean.valueOf(metadata.get("ui::textarea"))) { return new TextAreaWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::datetime")) { return new DateTimeConverter(schemas, properties, actions, jsonSchema, lang, metadata.get("ui::datetime")) .convert(CompletableFuture.completedFuture(context)); } return new TextWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } })); }  @Override CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs); CompletionStage<PropertyContext<?>> convertObject(final PropertyContext<?> outputContext,
            final Map<String, String> metadata, final UiSchema parentUiSchema, final AtomicInteger idGenerator); }
UiSchemaConverter implements PropertyConverter { @Override public CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs) { return cs .thenCompose(context -> customConverters .stream() .filter(it -> it.supports(context)) .findFirst() .map(it -> it .convert(cs, new CustomPropertyConverter.ConverterContext(family, schemas, includedProperties, client, jsonSchema, properties, actions, lang))) .orElseGet(() -> { final SimplePropertyDefinition property = context.getProperty(); final String type = property.getType().toLowerCase(Locale.ROOT); final Map<String, String> metadata = property.getMetadata(); switch (type) { case "object": return convertObject(context, metadata, null, idGenerator); case "boolean": includedProperties.add(property); return new ToggleWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "enum": includedProperties.add(property); return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "number": includedProperties.add(property); return new NumberWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); case "array": includedProperties.add(property); final String nestedPrefix = property.getPath() + "[]."; final int from = nestedPrefix.length(); final Collection<SimplePropertyDefinition> nested = properties .stream() .filter(prop -> prop.getPath().startsWith(nestedPrefix) && prop.getPath().indexOf('.', from) < 0) .collect(toList()); if (!nested.isEmpty()) { return new ObjectArrayWidgetConverter(schemas, properties, actions, family, client, gridLayoutFilter, jsonSchema, lang, customConverters, metadata, idGenerator) .convert(CompletableFuture.completedFuture(context)); } return new MultiSelectWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); case "string": default: if (property.getPath().endsWith("[]")) { return CompletableFuture.completedFuture(context); } includedProperties.add(property); if ("true".equalsIgnoreCase(metadata.get("ui::credential"))) { return new CredentialWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::code::value")) { return new CodeWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::suggestions") || metadata.containsKey("action::built_in_suggestable")) { return new SuggestionWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("action::dynamic_values")) { return new DataListWidgetConverter(schemas, properties, actions, client, family, jsonSchema, lang).convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::textarea") && Boolean.valueOf(metadata.get("ui::textarea"))) { return new TextAreaWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } else if (metadata.containsKey("ui::datetime")) { return new DateTimeConverter(schemas, properties, actions, jsonSchema, lang, metadata.get("ui::datetime")) .convert(CompletableFuture.completedFuture(context)); } return new TextWidgetConverter(schemas, properties, actions, jsonSchema, lang) .convert(CompletableFuture.completedFuture(context)); } })); }  @Override CompletionStage<PropertyContext<?>> convert(final CompletionStage<PropertyContext<?>> cs); CompletionStage<PropertyContext<?>> convertObject(final PropertyContext<?> outputContext,
            final Map<String, String> metadata, final UiSchema parentUiSchema, final AtomicInteger idGenerator);  }