View Javadoc
1   /*
2    * Copyright (C) 2023 B3Partners B.V.
3    *
4    * SPDX-License-Identifier: MIT
5    */
6   
7   package org.tailormap.api.persistence.helper;
8   
9   import jakarta.validation.constraints.NotNull;
10  import java.util.HashSet;
11  import java.util.LinkedHashMap;
12  import java.util.LinkedHashSet;
13  import java.util.Map;
14  import java.util.Optional;
15  import java.util.Set;
16  import java.util.stream.Collectors;
17  import org.apache.commons.lang3.tuple.Pair;
18  import org.tailormap.api.persistence.Application;
19  import org.tailormap.api.persistence.TMFeatureType;
20  import org.tailormap.api.persistence.json.AppLayerSettings;
21  import org.tailormap.api.persistence.json.AppTreeLayerNode;
22  import org.tailormap.api.persistence.json.AttributeSettings;
23  import org.tailormap.api.persistence.json.TMAttributeDescriptor;
24  
25  public class TMFeatureTypeHelper {
26    public static boolean isEditable(
27        Application application, AppTreeLayerNode appTreeLayerNode, TMFeatureType featureType) {
28      if (featureType == null) {
29        return false;
30      }
31  
32      boolean editable = false;
33      // Currently FeatureSourceHelper#loadCapabilities() sets editable to true when the datastore is
34      // a JDBC DataStore (even when the database account can't write to the table, can't detect this
35      // without trying). Other DataStore types we support (only WFS atm) we don't set as writeable
36  
37      // TODO: in the future, check for authorizations on editing. Currently you only need to be
38      // logged in (the viewer frontend already checks this before showing editable layers so we don't
39      // need to check for an authenticated user here).
40      if (featureType.isWriteable()) {
41        AppLayerSettings appLayerSettings = application.getAppLayerSettings(appTreeLayerNode);
42        editable = Boolean.TRUE.equals(appLayerSettings.getEditable());
43      }
44      return editable;
45    }
46  
47    public static Set<String> getHiddenAttributes(
48        @NotNull TMFeatureType featureType, @NotNull AppLayerSettings appLayerSettings) {
49      Set<String> hiddenAttributes = new HashSet<>();
50      Optional.ofNullable(featureType.getSettings().getHideAttributes())
51          .ifPresent(hiddenAttributes::addAll);
52      Optional.ofNullable(appLayerSettings.getHideAttributes()).ifPresent(hiddenAttributes::addAll);
53      return hiddenAttributes;
54    }
55  
56    public static Set<String> getReadOnlyAttributes(
57        @NotNull TMFeatureType featureType, @NotNull AppLayerSettings appLayerSettings) {
58      Set<String> readOnlyAttributes = new HashSet<>();
59      Optional.ofNullable(featureType.getSettings().getReadOnlyAttributes())
60          .ifPresent(readOnlyAttributes::addAll);
61      Optional.ofNullable(appLayerSettings.getReadOnlyAttributes())
62          .ifPresent(readOnlyAttributes::addAll);
63      return readOnlyAttributes;
64    }
65  
66    /**
67     * Return a map of attribute names (in order, using a LinkedHashMap implementation) to an
68     * attribute descriptor and configured settings pair, taking into account the configured attribute
69     * order and hidden attributes.
70     *
71     * @param featureType The feature type
72     * @param appLayerSettings The app layer settings
73     * @return A sorted map as described
74     */
75    public static Map<String, Pair<TMAttributeDescriptor, AttributeSettings>> getConfiguredAttributes(
76        @NotNull TMFeatureType featureType, @NotNull AppLayerSettings appLayerSettings) {
77      LinkedHashMap<String, TMAttributeDescriptor> originalAttributesOrder = new LinkedHashMap<>();
78      for (TMAttributeDescriptor attributeDescriptor : featureType.getAttributes()) {
79        originalAttributesOrder.put(attributeDescriptor.getName(), attributeDescriptor);
80      }
81  
82      // Order of attributes taking into account hidden attributes and configured attribute order
83      LinkedHashSet<String> finalAttributeOrder;
84  
85      if (featureType.getSettings().getAttributeOrder().isEmpty()) {
86        // Use original feature type order
87        finalAttributeOrder = new LinkedHashSet<>(originalAttributesOrder.keySet());
88      } else {
89        finalAttributeOrder = new LinkedHashSet<>(featureType.getSettings().getAttributeOrder());
90        // Remove once ordered attributes which no longer exist in the feature type as saved in the
91        // configuration database
92        finalAttributeOrder.retainAll(originalAttributesOrder.keySet());
93        // Add attributes not named in attributeOrder in feature type order (added to the feature type
94        // after an admin saved/changed the ordering of attributes -- these attributes should not be
95        // hidden).
96        if (finalAttributeOrder.size() != originalAttributesOrder.size()) {
97          finalAttributeOrder.addAll(originalAttributesOrder.keySet());
98        }
99      }
100 
101     getHiddenAttributes(featureType, appLayerSettings).forEach(finalAttributeOrder::remove);
102 
103     Map<String, AttributeSettings> attributeSettings =
104         featureType.getSettings().getAttributeSettings();
105     LinkedHashMap<String, Pair<TMAttributeDescriptor, AttributeSettings>> result =
106         new LinkedHashMap<>();
107     for (String attribute : finalAttributeOrder) {
108       AttributeSettings settings =
109           Optional.ofNullable(attributeSettings.get(attribute)).orElseGet(AttributeSettings::new);
110       TMAttributeDescriptor attributeDescriptor = originalAttributesOrder.get(attribute);
111       result.put(attribute, Pair.of(attributeDescriptor, settings));
112     }
113     return result;
114   }
115 
116   /**
117    * Get the non-hidden attribute descriptors for a feature type.
118    *
119    * @param featureType The feature type
120    * @param appLayerSettings The app layer settings
121    * @return Unordered set of attribute descriptors
122    */
123   public static Set<TMAttributeDescriptor> getNonHiddenAttributes(
124       @NotNull TMFeatureType featureType, @NotNull AppLayerSettings appLayerSettings) {
125     Set<String> hiddenAttributes = getHiddenAttributes(featureType, appLayerSettings);
126     return featureType.getAttributes().stream()
127         .filter(attributeDescriptor -> !hiddenAttributes.contains(attributeDescriptor.getName()))
128         .collect(Collectors.toSet());
129   }
130 
131   /**
132    * Get the non-hidden attribute names for a feature type.
133    *
134    * @param featureType The feature type
135    * @param appLayerSettings The app layer settings
136    * @return Unordered set of attribute names
137    */
138   public static Set<String> getNonHiddenAttributeNames(
139       @NotNull TMFeatureType featureType, @NotNull AppLayerSettings appLayerSettings) {
140     return getNonHiddenAttributes(featureType, appLayerSettings).stream()
141         .map(TMAttributeDescriptor::getName)
142         .collect(Collectors.toSet());
143   }
144 }