diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/AggregateStatsReaderGraphQl.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/AggregateStatsReaderGraphQl.java index b438efc4a..68726848c 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/AggregateStatsReaderGraphQl.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/AggregateStatsReaderGraphQl.java @@ -8,7 +8,6 @@ package de.ii.xtraplatform.features.graphql.app; import de.ii.xtraplatform.crs.domain.BoundingBox; -import de.ii.xtraplatform.crs.domain.CrsTransformer; import de.ii.xtraplatform.crs.domain.CrsTransformerFactory; import de.ii.xtraplatform.crs.domain.EpsgCrs; import de.ii.xtraplatform.crs.domain.OgcCrs; @@ -18,20 +17,19 @@ import de.ii.xtraplatform.streams.domain.Reactive; import de.ii.xtraplatform.streams.domain.Reactive.Stream; import java.util.List; +import java.util.Objects; import java.util.Optional; import org.threeten.extra.Interval; public class AggregateStatsReaderGraphQl implements AggregateStatsReader { - private final FeatureMetadata featureMetadata; - private final Optional crsTransformer; - public AggregateStatsReaderGraphQl( FeatureMetadata featureMetadata, CrsTransformerFactory crsTransformerFactory, EpsgCrs nativeCrs) { - this.featureMetadata = featureMetadata; - this.crsTransformer = crsTransformerFactory.getTransformer(OgcCrs.CRS84, nativeCrs, true); + Objects.requireNonNull(featureMetadata); + Objects.requireNonNull(crsTransformerFactory) + .getTransformer(OgcCrs.CRS84, Objects.requireNonNull(nativeCrs), true); } @Override diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQl.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQl.java index a7ed9bd2e..2f5c995fd 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQl.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQl.java @@ -375,7 +375,6 @@ public Optional getSpatialExtent(String typeName) { .join(); } catch (Throwable e) { // continue - boolean br = true; } return Optional.empty(); diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQlFactory.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQlFactory.java index a5b595941..cc3ba907a 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQlFactory.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureProviderGraphQlFactory.java @@ -25,6 +25,7 @@ import de.ii.xtraplatform.features.graphql.domain.FeatureProviderGraphQlData; import de.ii.xtraplatform.features.graphql.domain.ImmutableFeatureProviderGraphQlData; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import javax.inject.Inject; @@ -41,7 +42,6 @@ public class FeatureProviderGraphQlFactory private static final Logger LOGGER = LoggerFactory.getLogger(FeatureProviderGraphQlFactory.class); private final Lazy> schemaResolvers; - private final ConnectorFactory connectorFactory; @Inject public FeatureProviderGraphQlFactory( @@ -50,7 +50,7 @@ public FeatureProviderGraphQlFactory( ProviderGraphQlFactoryAssisted providerGraphQlFactoryAssisted) { super(providerGraphQlFactoryAssisted); this.schemaResolvers = schemaResolvers; - this.connectorFactory = connectorFactory; + Objects.requireNonNull(connectorFactory); } @Override diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureQueryEncoderGraphQl.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureQueryEncoderGraphQl.java index ec9f3a004..8ae46f585 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureQueryEncoderGraphQl.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureQueryEncoderGraphQl.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -39,7 +40,6 @@ public class FeatureQueryEncoderGraphQl implements FeatureQueryEncoder featureSchemas; private final Map> sourceSchemas; private final GraphQlQueries queryGeneration; - private final EpsgCrs nativeCrs; private final FilterEncoderGraphQl filterEncoder; public FeatureQueryEncoderGraphQl( @@ -52,8 +52,8 @@ public FeatureQueryEncoderGraphQl( Cql cql) { this.featureSchemas = featureSchemas; this.sourceSchemas = sourceSchemas; + Objects.requireNonNull(connectionInfo); this.queryGeneration = queryGeneration; - this.nativeCrs = nativeCrs; this.filterEncoder = new FilterEncoderGraphQl(nativeCrs, crsTransformerFactory, cql, queryGeneration); } @@ -95,22 +95,16 @@ private String getNestedFields(String sourcePath) { return sourcePath; } - String fields = ""; - String[] split = sourcePath.split("/"); - for (int i = split.length - 1; i >= 0; i--) { - String elem = split[i]; - if (!fields.isBlank()) { - elem = elem + " " + fields; - } - if (i > 0) { - fields = "{ " + elem + " }"; - } else { - fields = elem; - } + StringBuilder fields = new StringBuilder(split[split.length - 1]); + + for (int i = split.length - 2; i >= 0; i--) { + fields.insert(0, " { "); + fields.insert(0, split[i]); + fields.append(" }"); } - return fields; + return fields.toString(); } public String getFields(FeatureSchema featureSchema, String indentation) { @@ -146,7 +140,9 @@ public String encodeFeatureQuery( String q = String.format(queryTemplate, name + arguments, fields); - LOGGER.debug("GraphQL Request\n{}", q.replaceAll("\\\\n", "\n")); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("GraphQL Request\n{}", q.replaceAll("\\\\n", "\n")); + } return q; } @@ -174,7 +170,7 @@ private List getPaging(int limit, int offset) { .map( template -> StringTemplateFilters.applyTemplate( - template, (Map.of("value", String.valueOf(limit)))::get)) + template, Map.of("value", String.valueOf(limit))::get)) .ifPresent(arguments::add); queryGeneration @@ -184,7 +180,7 @@ private List getPaging(int limit, int offset) { .map( template -> StringTemplateFilters.applyTemplate( - template, (Map.of("value", String.valueOf(offset)))::get)) + template, Map.of("value", String.valueOf(offset))::get)) .ifPresent(arguments::add); return arguments; @@ -212,7 +208,7 @@ private List getFilter( String argument = StringTemplateFilters.applyTemplate( queryGeneration.getCollection().getArguments().getFilter().orElse("{{value}}"), - (Map.of("value", filterString))::get); + Map.of("value", filterString)::get); return List.of(argument); } diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureTokenDecoderGraphQlJson2.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureTokenDecoderGraphQlJson2.java index 95bf8ba0b..3f66fcabb 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureTokenDecoderGraphQlJson2.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FeatureTokenDecoderGraphQlJson2.java @@ -28,16 +28,12 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class FeatureTokenDecoderGraphQlJson2 extends FeatureTokenDecoder< byte[], FeatureSchema, SchemaMapping, ModifiableContext> implements Decoder.Pipeline { - private static final Logger LOGGER = - LoggerFactory.getLogger(FeatureTokenDecoderGraphQlJson2.class); private static final JsonFactory JSON_FACTORY = new JsonFactory(); private final JsonParser parser; @@ -47,23 +43,21 @@ public class FeatureTokenDecoderGraphQlJson2 private final Map mappings; private final String type; private final String wrapper; - private final Optional nullValue; - private boolean started; - private int depth = -1; - private int featureDepth = 0; - private boolean inFeatures = false; - private boolean inProperties = false; - private boolean isCollection = false; - private boolean inErrors = false; - private int lastNameIsArrayDepth = 0; - private int startArray = 0; - private int endArray = 0; + private final ParsingState state; private ModifiableContext context; - private List> arrayPaths; private DecoderJsonProperties decoderJsonProperties; + private static final class ParsingState { + boolean started; + int featureDepth; + boolean inFeatures; + boolean inProperties; + boolean isCollection; + boolean inErrors; + } + public FeatureTokenDecoderGraphQlJson2( FeatureSchema featureSchema, FeatureQuery query, @@ -80,6 +74,7 @@ public FeatureTokenDecoderGraphQlJson2( String type, String wrapper, Optional nullValue) { + super(); try { this.parser = JSON_FACTORY.createNonBlockingByteArrayParser(); } catch (IOException e) { @@ -91,7 +86,8 @@ public FeatureTokenDecoderGraphQlJson2( this.mappings = mappings; this.type = type; this.wrapper = wrapper; - this.nullValue = nullValue; + this.state = new ParsingState(); + Objects.requireNonNull(nullValue); } @Override @@ -102,7 +98,7 @@ protected void init() { .setMappings(mappings) .setQuery(featureQuery); - this.arrayPaths = + List> arrayPaths = context.mapping().getSchemasByTargetPath().entrySet().stream() .filter(entry -> entry.getValue().get(0).isArray()) .map(entry -> entry.getKey()) @@ -128,7 +124,7 @@ public void onPush(byte[] bytes) { } // for unit tests - void parse(String data) throws Exception { + void parse(String data) { byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); feedInput(dataBytes); cleanup(); @@ -147,6 +143,7 @@ private void feedInput(byte[] data) { } } + @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CyclomaticComplexity"}) public boolean advanceParser() { boolean feedMeMore = false; @@ -165,44 +162,44 @@ public boolean advanceParser() { case START_OBJECT: case START_ARRAY: - if (!inProperties) { - if (inFeatures && context.path().size() == featureDepth) { - this.inProperties = true; - if (isCollection) { + if (!state.inProperties) { + if (state.inFeatures && context.path().size() == state.featureDepth) { + state.inProperties = true; + if (state.isCollection) { startFeature(); } - } else if (!inFeatures && Objects.equals(currentName, wrapper)) { + } else if (!state.inFeatures && Objects.equals(currentName, wrapper)) { startIfNecessary(nextToken == JsonToken.START_ARRAY); - } else if (!inFeatures && Objects.equals(currentName, "errors")) { - this.inErrors = true; + } else if (!state.inFeatures && Objects.equals(currentName, "errors")) { + state.inErrors = true; } break; } - feedMeMore = decoderJsonProperties.parse(nextToken, currentName, featureDepth); + feedMeMore = decoderJsonProperties.parse(nextToken, currentName, state.featureDepth); break; case END_OBJECT: - if (inProperties && context.path().size() == featureDepth) { - this.inProperties = false; + if (state.inProperties && context.path().size() == state.featureDepth) { + state.inProperties = false; getDownstream().onFeatureEnd(context); - if (!isCollection) { + if (!state.isCollection) { getDownstream().onEnd(context); - this.inFeatures = false; + state.inFeatures = false; } break; } - feedMeMore = decoderJsonProperties.parse(nextToken, currentName, featureDepth); + feedMeMore = decoderJsonProperties.parse(nextToken, currentName, state.featureDepth); break; case END_ARRAY: - if (!inProperties && context.path().size() == featureDepth) { + if (!state.inProperties && context.path().size() == state.featureDepth) { getDownstream().onEnd(context); - this.inFeatures = false; + state.inFeatures = false; break; } - feedMeMore = decoderJsonProperties.parse(nextToken, currentName, featureDepth); + feedMeMore = decoderJsonProperties.parse(nextToken, currentName, state.featureDepth); break; default: - if (!inErrors) { - feedMeMore = decoderJsonProperties.parse(nextToken, currentName, featureDepth); + if (!state.inErrors) { + feedMeMore = decoderJsonProperties.parse(nextToken, currentName, state.featureDepth); break; } if (Objects.equals(currentName, "message")) { @@ -231,10 +228,10 @@ public ModifiableContext context() { } private void startIfNecessary(boolean isCollection) { - if (!started) { - this.started = true; - this.inFeatures = true; - this.isCollection = isCollection; + if (!state.started) { + state.started = true; + state.inFeatures = true; + state.isCollection = isCollection; if (!isCollection) { context.metadata().isSingleFeature(true); @@ -251,7 +248,7 @@ private void startFeature() { context.setIndexes(List.of()); decoderJsonProperties.reset(); getDownstream().onFeatureStart(context); - this.featureDepth = context.path().size(); - this.inProperties = true; + state.featureDepth = context.path().size(); + state.inProperties = true; } } diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FilterEncoderGraphQl.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FilterEncoderGraphQl.java index de86ce477..3a91adeb1 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FilterEncoderGraphQl.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/FilterEncoderGraphQl.java @@ -51,14 +51,10 @@ import java.util.Optional; import java.util.function.BiFunction; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +@SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"}) public class FilterEncoderGraphQl { - private static final Logger LOGGER = LoggerFactory.getLogger(FilterEncoderGraphQl.class); - - private final Cql cql; private final EpsgCrs nativeCrs; private final CrsTransformerFactory crsTransformerFactory; BiFunction, Optional, List> coordinatesTransformer; @@ -71,7 +67,7 @@ public FilterEncoderGraphQl( GraphQlQueries queryGeneration) { this.nativeCrs = nativeCrs; this.crsTransformerFactory = crsTransformerFactory; - this.cql = cql; + Objects.requireNonNull(cql); this.coordinatesTransformer = this::transformCoordinatesIfNecessary; this.queryGeneration = queryGeneration; } @@ -82,23 +78,25 @@ public Map encode(Cql2Expression filter, FeatureSchema schema) { private List transformCoordinatesIfNecessary( List coordinates, Optional sourceCrs) { + if (sourceCrs.isEmpty() || Objects.equals(sourceCrs.get(), nativeCrs)) { + return coordinates; + } - if (sourceCrs.isPresent() && !Objects.equals(sourceCrs.get(), nativeCrs)) { - Optional transformer = - crsTransformerFactory.getTransformer(sourceCrs.get(), nativeCrs, true); - if (transformer.isPresent()) { - double[] transformed = - transformer.get().transform(Doubles.toArray(coordinates), coordinates.size() / 2, 2); - if (Objects.isNull(transformed)) { - throw new IllegalArgumentException( - String.format( - "Filter is invalid. Coordinates cannot be transformed: %s", coordinates)); - } + Optional transformer = + crsTransformerFactory.getTransformer(sourceCrs.get(), nativeCrs, true); - return Doubles.asList(transformed); - } + if (transformer.isEmpty()) { + return coordinates; } - return coordinates; + + double[] transformed = + transformer.get().transform(Doubles.toArray(coordinates), coordinates.size() / 2, 2); + if (Objects.isNull(transformed)) { + throw new IllegalArgumentException( + String.format("Filter is invalid. Coordinates cannot be transformed: %s", coordinates)); + } + + return Doubles.asList(transformed); } // TODO: reverse FeatureSchema for nested mappings? @@ -106,16 +104,16 @@ private Optional getPrefixedPropertyName(FeatureSchema schema, String pr return schema.getProperties().stream() .filter( featureProperty -> { - if (Objects.nonNull(featureProperty.getName())) { - if (Objects.equals( - featureProperty.getName().toLowerCase(), property.toLowerCase())) { - return true; - } - if (Objects.equals(property, ID_PLACEHOLDER) && featureProperty.isId()) { - return true; - } + String featurePropertyName = featureProperty.getName(); + if (Objects.isNull(featurePropertyName)) { + return false; } - return false; + + if (featurePropertyName.equalsIgnoreCase(property)) { + return true; + } + + return Objects.equals(property, ID_PLACEHOLDER) && featureProperty.isId(); }) .map(FeatureSchema::getSourcePath) .filter(Optional::isPresent) @@ -195,7 +193,7 @@ public Map visit(In in, List> children) { String argument = StringTemplateFilters.applyTemplate( - template.get(), (Map.of("sourcePath", sourcePath, "value", value))::get); + template.get(), Map.of("sourcePath", sourcePath, "value", value)::get); return fromString(argument); } @@ -248,7 +246,7 @@ public Map visit( String argument = StringTemplateFilters.applyTemplate( queryGeneration.getCollection().getArguments().getBbox().get(), - (Map.of("sourcePath", sourcePath, "value", wkt))::get); + Map.of("sourcePath", sourcePath, "value", wkt)::get); return fromString(argument); } @@ -348,7 +346,7 @@ public Map visit(Bbox bbox, List> children) String argument = StringTemplateFilters.applyTemplate( queryGeneration.getCollection().getArguments().getGeometry().get(), - (Map.of("value", wkt))::get, + Map.of("value", wkt)::get, Map.of("toWkt", java.util.function.Function.identity())); return fromString(argument); @@ -401,6 +399,7 @@ static Map fromString(String obj) { return fromString(obj, false); } + @SuppressWarnings("PMD.CyclomaticComplexity") static Map fromString(String obj, boolean subField) { String separator = subField ? " " : ":"; String cleaned = obj.trim(); diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/SchemaDeriverGraphQl.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/SchemaDeriverGraphQl.java index ab1fdcfea..bb00dc479 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/SchemaDeriverGraphQl.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/app/SchemaDeriverGraphQl.java @@ -46,7 +46,7 @@ public FeatureSchema visit( .map( template -> StringTemplateFilters.applyTemplate( - template, (Map.of("sourcePath", path))::get)) + template, (String key) -> Map.of("sourcePath", path).get(key))) .map(expr -> FilterEncoderGraphQl.fromString(expr, true)) .map(expr -> path + "/" + expr.get(path))); } else if (schema.isFeatureRef()) { diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/domain/GraphQlQueries.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/domain/GraphQlQueries.java index ffdca6da0..2dc300a7a 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/domain/GraphQlQueries.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/domain/GraphQlQueries.java @@ -45,7 +45,7 @@ interface SingleQuery { String getName(); default String getName(String type) { - return StringTemplateFilters.applyTemplate(getName(), (Map.of("type", type))::get); + return StringTemplateFilters.applyTemplate(getName(), Map.of("type", type)::get); } /** @@ -76,7 +76,7 @@ interface CollectionQuery { String getName(); default String getName(String type) { - return StringTemplateFilters.applyTemplate(getName(), (Map.of("type", type))::get); + return StringTemplateFilters.applyTemplate(getName(), Map.of("type", type)::get); } /** diff --git a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/infra/GraphQlConnectorHttp.java b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/infra/GraphQlConnectorHttp.java index 5018875b3..46b3c6638 100644 --- a/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/infra/GraphQlConnectorHttp.java +++ b/xtraplatform-features-graphql/src/main/java/de/ii/xtraplatform/features/graphql/infra/GraphQlConnectorHttp.java @@ -117,14 +117,12 @@ public Optional getConnectionError() { @Override public Reactive.Source getSourceStream(String query) { - InputStream inputStream = + try (InputStream inputStream = httpClient.postAsInputStream( connectionInfo.getUri().toString(), query.getBytes(StandardCharsets.UTF_8), MediaType.APPLICATION_JSON_TYPE, - Map.of("Accept", MediaType.APPLICATION_JSON)); - - try { + Map.of("Accept", MediaType.APPLICATION_JSON))) { byte[] bytes = inputStream.readAllBytes(); LOGGER.debug("Response \n{}", new String(bytes, StandardCharsets.UTF_8));