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.api.referencing.crs.CoordinateReferenceSystem;
26 import org.geotools.referencing.CRS;
27 import org.hibernate.annotations.JdbcTypeCode;
28 import org.hibernate.envers.Audited;
29 import org.hibernate.type.SqlTypes;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
33 import org.tailormap.api.persistence.json.AppContent;
34 import org.tailormap.api.persistence.json.AppLayerSettings;
35 import org.tailormap.api.persistence.json.AppSettings;
36 import org.tailormap.api.persistence.json.AppTreeLayerNode;
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
43 @Audited
44 @Entity
45 @EntityListeners({EntityEventPublisher.class, AuditingEntityListener.class})
46 public class Application extends AuditMetadata {
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 @JdbcTypeCode(SqlTypes.JSON)
89 @Column(columnDefinition = "jsonb")
90 @NotNull private AppContent contentRoot = new AppContent();
91
92 @JsonIgnore
93 private transient AppContent oldContentRoot;
94
95 @JdbcTypeCode(SqlTypes.JSON)
96 @Column(columnDefinition = "jsonb")
97 @NotNull private AppSettings settings = new AppSettings();
98
99 @JdbcTypeCode(SqlTypes.JSON)
100 @Column(columnDefinition = "jsonb")
101 @NotNull private List<Component> components = new ArrayList<>();
102
103 @JdbcTypeCode(SqlTypes.JSON)
104 @Column(columnDefinition = "jsonb")
105 @NotNull private AppStyling styling = new AppStyling();
106
107 @JdbcTypeCode(SqlTypes.JSON)
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 CoordinateReferenceSystem getGeoToolsCoordinateReferenceSystem() {
287 CoordinateReferenceSystem gtCrs = null;
288 try {
289 if (getCrs() != null) {
290 gtCrs = CRS.decode(getCrs());
291 }
292 } catch (Exception e) {
293 String message = "Application %d: error decoding CRS from code \"%s\": %s: %s"
294 .formatted(getId(), getCrs(), e.getClass(), e.getMessage());
295 if (logger.isDebugEnabled()) {
296 logger.error(message, e);
297 } else {
298 logger.error(message);
299 }
300 }
301 return gtCrs;
302 }
303
304 @NotNull public AppLayerSettings getAppLayerSettings(@NotNull AppTreeLayerNode node) {
305 return getAppLayerSettings(node.getId());
306 }
307
308 @NotNull public AppLayerSettings getAppLayerSettings(@NotNull String appLayerId) {
309 return Optional.ofNullable(getSettings())
310 .map(AppSettings::getLayerSettings)
311 .map(layerSettingsMap -> layerSettingsMap.get(appLayerId))
312 .orElseGet(AppLayerSettings::new);
313 }
314 }