1
2
3
4
5
6 package org.tailormap.api.persistence;
7
8 import static java.util.Objects.requireNonNullElse;
9
10 import com.fasterxml.jackson.annotation.JsonIgnore;
11 import jakarta.persistence.AttributeOverride;
12 import jakarta.persistence.AttributeOverrides;
13 import jakarta.persistence.Column;
14 import jakarta.persistence.Embedded;
15 import jakarta.persistence.Entity;
16 import jakarta.persistence.EntityListeners;
17 import jakarta.persistence.GeneratedValue;
18 import jakarta.persistence.GenerationType;
19 import jakarta.persistence.Id;
20 import jakarta.persistence.Version;
21 import jakarta.validation.constraints.NotNull;
22 import java.lang.invoke.MethodHandles;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.stream.Stream;
27 import org.geotools.referencing.CRS;
28 import org.hibernate.annotations.Type;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.tailormap.api.persistence.json.AppContent;
32 import org.tailormap.api.persistence.json.AppI18nSettings;
33 import org.tailormap.api.persistence.json.AppLayerSettings;
34 import org.tailormap.api.persistence.json.AppSettings;
35 import org.tailormap.api.persistence.json.AppTreeLayerNode;
36 import org.tailormap.api.persistence.json.AppUiSettings;
37 import org.tailormap.api.persistence.json.AuthorizationRule;
38 import org.tailormap.api.persistence.json.Bounds;
39 import org.tailormap.api.persistence.listener.EntityEventPublisher;
40 import org.tailormap.api.viewer.model.AppStyling;
41 import org.tailormap.api.viewer.model.Component;
42 import org.tailormap.api.viewer.model.ViewerResponse;
43
44 @Entity
45 @EntityListeners(EntityEventPublisher.class)
46 public class Application {
47 private static final Logger logger =
48 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
49
50 @Id
51 @GeneratedValue(strategy = GenerationType.IDENTITY)
52 private Long id;
53
54 @Version
55 private Long version;
56
57 @Column(unique = true)
58 @NotNull private String name;
59
60 private String title;
61
62 @Column(columnDefinition = "text")
63 private String adminComments;
64
65 @Column(columnDefinition = "text")
66 private String previewText;
67
68 @NotNull private String crs;
69
70 @Embedded
71 @AttributeOverrides({
72 @AttributeOverride(name = "minx", column = @Column(name = "initial_minx")),
73 @AttributeOverride(name = "maxx", column = @Column(name = "initial_maxx")),
74 @AttributeOverride(name = "miny", column = @Column(name = "initial_miny")),
75 @AttributeOverride(name = "maxy", column = @Column(name = "initial_maxy"))
76 })
77 private Bounds initialExtent;
78
79 @Embedded
80 @AttributeOverrides({
81 @AttributeOverride(name = "minx", column = @Column(name = "max_minx")),
82 @AttributeOverride(name = "maxx", column = @Column(name = "max_maxx")),
83 @AttributeOverride(name = "miny", column = @Column(name = "max_miny")),
84 @AttributeOverride(name = "maxy", column = @Column(name = "max_maxy"))
85 })
86 private Bounds maxExtent;
87
88 @Type(value = io.hypersistence.utils.hibernate.type.json.JsonBinaryType.class)
89 @Column(columnDefinition = "jsonb")
90 @NotNull private AppContent contentRoot = new AppContent();
91
92 @JsonIgnore
93 private transient AppContent oldContentRoot;
94
95 @Type(value = io.hypersistence.utils.hibernate.type.json.JsonBinaryType.class)
96 @Column(columnDefinition = "jsonb")
97 @NotNull private AppSettings settings = new AppSettings();
98
99 @Type(value = io.hypersistence.utils.hibernate.type.json.JsonBinaryType.class)
100 @Column(columnDefinition = "jsonb")
101 @NotNull private List<Component> components = new ArrayList<>();
102
103 @Type(value = io.hypersistence.utils.hibernate.type.json.JsonBinaryType.class)
104 @Column(columnDefinition = "jsonb")
105 @NotNull private AppStyling styling = new AppStyling();
106
107 @Type(value = io.hypersistence.utils.hibernate.type.json.JsonBinaryType.class)
108 @Column(columnDefinition = "jsonb")
109 @NotNull private List<AuthorizationRule> authorizationRules = new ArrayList<>();
110
111
112 public Long getId() {
113 return id;
114 }
115
116 public Application setId(Long id) {
117 this.id = id;
118 return this;
119 }
120
121 public Long getVersion() {
122 return version;
123 }
124
125 public Application setVersion(Long version) {
126 this.version = version;
127 return this;
128 }
129
130 public String getName() {
131 return name;
132 }
133
134 public Application setName(String name) {
135 this.name = name;
136 return this;
137 }
138
139 public String getTitle() {
140 return title;
141 }
142
143 public Application setTitle(String title) {
144 this.title = title;
145 return this;
146 }
147
148 public String getAdminComments() {
149 return adminComments;
150 }
151
152 public Application setAdminComments(String adminComments) {
153 this.adminComments = adminComments;
154 return this;
155 }
156
157 public String getPreviewText() {
158 return previewText;
159 }
160
161 public Application setPreviewText(String previewText) {
162 this.previewText = previewText;
163 return this;
164 }
165
166 public String getCrs() {
167 return crs;
168 }
169
170 public Application setCrs(String crs) {
171 this.crs = crs;
172 return this;
173 }
174
175 public Bounds getInitialExtent() {
176 return initialExtent;
177 }
178
179 public Application setInitialExtent(Bounds initialExtent) {
180 this.initialExtent = initialExtent;
181 return this;
182 }
183
184 public Bounds getMaxExtent() {
185 return maxExtent;
186 }
187
188 public Application setMaxExtent(Bounds maxExtent) {
189 this.maxExtent = maxExtent;
190 return this;
191 }
192
193 public AppContent getContentRoot() {
194 return contentRoot;
195 }
196
197 public Application setContentRoot(AppContent contentRoot) {
198 this.oldContentRoot = this.contentRoot;
199 this.contentRoot = contentRoot;
200 return this;
201 }
202
203
204
205
206
207
208 public AppContent getOldContentRoot() {
209 return this.oldContentRoot;
210 }
211
212 public AppSettings getSettings() {
213 return settings;
214 }
215
216 public Application setSettings(AppSettings layerSettings) {
217 this.settings = layerSettings;
218 return this;
219 }
220
221 public List<Component> getComponents() {
222 return components;
223 }
224
225 public Application setComponents(List<Component> components) {
226 this.components = components;
227 return this;
228 }
229
230 public AppStyling getStyling() {
231 return styling;
232 }
233
234 public Application setStyling(AppStyling styling) {
235 this.styling = styling;
236 return this;
237 }
238
239 public List<AuthorizationRule> getAuthorizationRules() {
240 return authorizationRules;
241 }
242
243 public Application setAuthorizationRules(List<AuthorizationRule> authorizationRules) {
244 this.authorizationRules = authorizationRules;
245 return this;
246 }
247
248
249 @JsonIgnore
250 public Stream<AppTreeLayerNode> getAllOldAppTreeLayerNode() {
251 return this.getAppTreeLayerNodeStream(this.getOldContentRoot());
252 }
253
254 @JsonIgnore
255 public Stream<AppTreeLayerNode> getAllAppTreeLayerNode() {
256 return this.getAppTreeLayerNodeStream(this.getContentRoot());
257 }
258
259 @JsonIgnore
260 private Stream<AppTreeLayerNode> getAppTreeLayerNodeStream(AppContent contentRoot) {
261 if (contentRoot == null) {
262 return Stream.empty();
263 }
264 Stream<AppTreeLayerNode> baseLayers = Stream.empty();
265 if (contentRoot.getBaseLayerNodes() != null) {
266 baseLayers = contentRoot.getBaseLayerNodes().stream()
267 .filter(n -> "AppTreeLayerNode".equals(n.getObjectType()))
268 .map(n -> (AppTreeLayerNode) n);
269 }
270 Stream<AppTreeLayerNode> layers = Stream.empty();
271 if (contentRoot.getLayerNodes() != null) {
272 layers = contentRoot.getLayerNodes().stream()
273 .filter(n -> "AppTreeLayerNode".equals(n.getObjectType()))
274 .map(n -> (AppTreeLayerNode) n);
275 }
276 return Stream.concat(baseLayers, layers);
277 }
278
279
280
281
282
283
284
285 @JsonIgnore
286 public org.geotools.api.referencing.crs.CoordinateReferenceSystem getGeoToolsCoordinateReferenceSystem() {
287 org.geotools.api.referencing.crs.CoordinateReferenceSystem gtCrs = null;
288 try {
289 if (getCrs() != null) {
290 gtCrs = CRS.decode(getCrs());
291 }
292 } catch (Exception e) {
293 String message = String.format(
294 "Application %d: error decoding CRS from code \"%s\": %s: %s",
295 getId(), getCrs(), e.getClass(), e.getMessage());
296 if (logger.isDebugEnabled()) {
297 logger.error(message, e);
298 } else {
299 logger.error(message);
300 }
301 }
302 return gtCrs;
303 }
304
305 @JsonIgnore
306 public ViewerResponse getViewerResponse() {
307 return new ViewerResponse()
308 .kind(ViewerResponse.KindEnum.APP)
309 .name(getName())
310 .title(getTitle())
311 .styling(styling)
312 .components(components)
313 .i18nSettings(requireNonNullElse(
314 settings.getI18nSettings(), new AppI18nSettings().hideLanguageSwitcher(false)))
315 .uiSettings(requireNonNullElse(settings.getUiSettings(), new AppUiSettings().hideLoginButton(false)))
316 .projections(List.of(getCrs()))
317 .filterGroups(settings.getFilterGroups());
318 }
319
320 @NotNull public AppLayerSettings getAppLayerSettings(@NotNull AppTreeLayerNode node) {
321 return Optional.ofNullable(getSettings())
322 .map(AppSettings::getLayerSettings)
323 .map(layerSettingsMap -> layerSettingsMap.get(node.getId()))
324 .orElseGet(AppLayerSettings::new);
325 }
326 }