001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.geoimage.viewers.projections;
003
004import java.util.EnumMap;
005import java.util.Map;
006import java.util.Objects;
007import java.util.stream.Collectors;
008
009import org.openstreetmap.josm.data.imagery.street_level.Projections;
010import org.openstreetmap.josm.tools.JosmRuntimeException;
011
012/**
013 * A class that holds a registry of viewers for image projections
014 * @since 18246
015 */
016public final class ImageProjectionRegistry {
017    private static final EnumMap<Projections, Class<? extends IImageViewer>> DEFAULT_VIEWERS = new EnumMap<>(Projections.class);
018
019    // Register the default viewers
020    static {
021        try {
022            registerViewer(Perspective.class);
023            registerViewer(Equirectangular.class);
024        } catch (ReflectiveOperationException e) {
025            throw new JosmRuntimeException(e);
026        }
027    }
028
029    private ImageProjectionRegistry() {
030        // Prevent instantiations
031    }
032
033    /**
034     * Register a new viewer
035     * @param clazz The class to register. The class <i>must</i> have a no args constructor
036     * @return {@code true} if something changed
037     * @throws ReflectiveOperationException if there is no no-args constructor, or it is not visible to us.
038     */
039    public static boolean registerViewer(Class<? extends IImageViewer> clazz) throws ReflectiveOperationException {
040        Objects.requireNonNull(clazz, "null classes are hard to instantiate");
041        final IImageViewer object = clazz.getConstructor().newInstance();
042        boolean changed = false;
043        for (Projections projections : object.getSupportedProjections()) {
044            changed = clazz.equals(DEFAULT_VIEWERS.put(projections, clazz)) || changed;
045        }
046        return changed;
047    }
048
049    /**
050     * Remove a viewer
051     * @param clazz The class to remove.
052     * @return {@code true} if something changed
053     */
054    public static boolean removeViewer(Class<? extends IImageViewer> clazz) {
055        boolean changed = false;
056        for (Projections projections : DEFAULT_VIEWERS.entrySet().stream()
057                .filter(entry -> entry.getValue().equals(clazz)).map(Map.Entry::getKey)
058                .collect(Collectors.toList())) {
059            changed = DEFAULT_VIEWERS.remove(projections, clazz) || changed;
060        }
061        return changed;
062    }
063
064    /**
065     * Get the viewer for a specific projection type
066     * @param projection The projection to view
067     * @return The class to use
068     */
069    public static Class<? extends IImageViewer> getViewer(Projections projection) {
070        return DEFAULT_VIEWERS.getOrDefault(projection, DEFAULT_VIEWERS.getOrDefault(Projections.UNKNOWN, Perspective.class));
071    }
072}