1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.projection;
|
---|
3 |
|
---|
4 | import java.lang.ref.WeakReference;
|
---|
5 | import java.util.List;
|
---|
6 | import java.util.Objects;
|
---|
7 | import java.util.concurrent.CopyOnWriteArrayList;
|
---|
8 |
|
---|
9 | import org.openstreetmap.josm.data.Bounds;
|
---|
10 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
---|
11 |
|
---|
12 | /**
|
---|
13 | * Registry for a single, global projection instance.
|
---|
14 | * @since 14120
|
---|
15 | */
|
---|
16 | public final class ProjectionRegistry {
|
---|
17 |
|
---|
18 | /**
|
---|
19 | * The projection method used.
|
---|
20 | * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
|
---|
21 | * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
|
---|
22 | */
|
---|
23 | private static volatile Projection proj;
|
---|
24 |
|
---|
25 | private static ProjectionBoundsProvider boundsProvider;
|
---|
26 |
|
---|
27 | /*
|
---|
28 | * Keep WeakReferences to the listeners. This relieves clients from the burden of
|
---|
29 | * explicitly removing the listeners and allows us to transparently register every
|
---|
30 | * created dataset as projection change listener.
|
---|
31 | */
|
---|
32 | private static final List<WeakReference<ProjectionChangeListener>> listeners = new CopyOnWriteArrayList<>();
|
---|
33 |
|
---|
34 | private ProjectionRegistry() {
|
---|
35 | // hide constructor
|
---|
36 | }
|
---|
37 |
|
---|
38 | /**
|
---|
39 | * Replies the current projection.
|
---|
40 | *
|
---|
41 | * @return the currently active projection
|
---|
42 | */
|
---|
43 | public static Projection getProjection() {
|
---|
44 | return proj;
|
---|
45 | }
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * Sets the current projection
|
---|
49 | *
|
---|
50 | * @param p the projection
|
---|
51 | */
|
---|
52 | public static void setProjection(Projection p) {
|
---|
53 | CheckParameterUtil.ensureParameterNotNull(p);
|
---|
54 | Projection oldValue = proj;
|
---|
55 | Bounds b = boundsProvider != null ? boundsProvider.getRealBounds() : null;
|
---|
56 | proj = p;
|
---|
57 | fireProjectionChanged(oldValue, proj, b);
|
---|
58 | }
|
---|
59 |
|
---|
60 | private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
|
---|
61 | if ((newValue == null ^ oldValue == null)
|
---|
62 | || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
|
---|
63 | listeners.removeIf(x -> x.get() == null);
|
---|
64 | listeners.stream().map(WeakReference::get).filter(Objects::nonNull).forEach(x -> x.projectionChanged(oldValue, newValue));
|
---|
65 | if (newValue != null && oldBounds != null && boundsProvider != null) {
|
---|
66 | boundsProvider.restoreOldBounds(oldBounds);
|
---|
67 | }
|
---|
68 | /* TODO - remove layers with fixed projection */
|
---|
69 | }
|
---|
70 | }
|
---|
71 |
|
---|
72 | /**
|
---|
73 | * Register a projection change listener.
|
---|
74 | * The listener is registered to be weak, so keep a reference of it if you want it to be preserved.
|
---|
75 | *
|
---|
76 | * @param listener the listener. Ignored if <code>null</code>.
|
---|
77 | */
|
---|
78 | public static void addProjectionChangeListener(ProjectionChangeListener listener) {
|
---|
79 | if (listener == null) return;
|
---|
80 | for (WeakReference<ProjectionChangeListener> wr : listeners) {
|
---|
81 | // already registered ? => abort
|
---|
82 | if (wr.get() == listener) return;
|
---|
83 | }
|
---|
84 | listeners.add(new WeakReference<>(listener));
|
---|
85 | }
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Removes a projection change listener.
|
---|
89 | *
|
---|
90 | * @param listener the listener. Ignored if <code>null</code>.
|
---|
91 | */
|
---|
92 | public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
|
---|
93 | if (listener == null) return;
|
---|
94 | // remove the listener - and any other listener which got garbage collected in the meantime
|
---|
95 | listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
|
---|
96 | }
|
---|
97 |
|
---|
98 | /**
|
---|
99 | * Remove all projection change listeners. For testing purposes only.
|
---|
100 | */
|
---|
101 | public static void clearProjectionChangeListeners() {
|
---|
102 | listeners.clear();
|
---|
103 | }
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * Returns the bounds provider called in projection events.
|
---|
107 | * @return the bounds provider
|
---|
108 | */
|
---|
109 | public static ProjectionBoundsProvider getBoundsProvider() {
|
---|
110 | return boundsProvider;
|
---|
111 | }
|
---|
112 |
|
---|
113 | /**
|
---|
114 | * Sets the bounds provider called in projection events. Must not be null
|
---|
115 | * @param provider the bounds provider
|
---|
116 | */
|
---|
117 | public static void setboundsProvider(ProjectionBoundsProvider provider) {
|
---|
118 | boundsProvider = Objects.requireNonNull(provider);
|
---|
119 | }
|
---|
120 | }
|
---|