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