/******************************************************************************* * Copyright 2013 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.layer; import gov.nasa.worldwind.layers.Layer; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.RegistryFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.gov.ga.earthsci.common.util.ExtensionPointHelper; import au.gov.ga.earthsci.layer.wrappers.ILayerWrapper; /** * Manages the list of layers/wrappers/replacements defined using the * {@value #LAYERS_ID} extension point. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class ExtensionManager { private static final String LAYERS_ID = "au.gov.ga.earthsci.layers"; //$NON-NLS-1$ private static final Logger logger = LoggerFactory.getLogger(ExtensionManager.class); private static ExtensionManager instance = new ExtensionManager(); /** * @return An instance of the layer extension manager */ public static ExtensionManager getInstance() { return instance; } private final Map<String, Class<? extends IPersistentLayer>> idToLayer = new HashMap<String, Class<? extends IPersistentLayer>>(); private final Map<Class<? extends IPersistentLayer>, String> layerToId = new HashMap<Class<? extends IPersistentLayer>, String>(); private final Map<String, Class<? extends ILayerWrapper>> idToWrapper = new HashMap<String, Class<? extends ILayerWrapper>>(); private final Map<Class<? extends ILayerWrapper>, String> wrapperToId = new HashMap<Class<? extends ILayerWrapper>, String>(); private final Map<Class<? extends Layer>, Class<? extends ILayerWrapper>> layerToWrapper = new HashMap<Class<? extends Layer>, Class<? extends ILayerWrapper>>(); private final Map<Class<? extends ILayerWrapper>, Class<? extends Layer>> wrapperToLayer = new HashMap<Class<? extends ILayerWrapper>, Class<? extends Layer>>(); private final Map<String, Class<? extends IPersistentLayer>> replacementClasses = new HashMap<String, Class<? extends IPersistentLayer>>(); private final Map<String, String> replacementIds = new HashMap<String, String>(); private final Map<Class<? extends Layer>, Integer> drawOrders = new HashMap<Class<? extends Layer>, Integer>(); private ExtensionManager() { IConfigurationElement[] elements = RegistryFactory.getRegistry().getConfigurationElementsFor(LAYERS_ID); for (IConfigurationElement element : elements) { try { if ("layer".equals(element.getName())) //$NON-NLS-1$ { String id = element.getAttribute("id"); //$NON-NLS-1$ if (idToLayer.containsKey(id) || idToWrapper.containsKey(id)) { throw new IllegalArgumentException("Layer id '" + id + "' already exists"); //$NON-NLS-1$ //$NON-NLS-2$ } @SuppressWarnings("unchecked") Class<? extends IPersistentLayer> layer = (Class<? extends IPersistentLayer>) ExtensionPointHelper.getClassForProperty(element, "class"); //$NON-NLS-1$ idToLayer.put(id, layer); layerToId.put(layer, id); } else if ("wrapper".equals(element.getName())) //$NON-NLS-1$ { String id = element.getAttribute("id"); //$NON-NLS-1$ if (idToLayer.containsKey(id) || idToWrapper.containsKey(id)) { throw new IllegalArgumentException("Layer id '" + id + "' already exists"); //$NON-NLS-1$ //$NON-NLS-2$ } @SuppressWarnings("unchecked") Class<? extends ILayerWrapper> wrapper = (Class<? extends ILayerWrapper>) ExtensionPointHelper.getClassForProperty(element, "class"); //$NON-NLS-1$ @SuppressWarnings("unchecked") Class<? extends Layer> layer = (Class<? extends Layer>) ExtensionPointHelper.getClassForProperty(element, "wraps"); //$NON-NLS-1$ idToWrapper.put(id, wrapper); wrapperToId.put(wrapper, id); layerToWrapper.put(layer, wrapper); wrapperToLayer.put(wrapper, layer); } else if ("replacementClass".equals(element.getName())) //$NON-NLS-1$ { String replacementFor = element.getAttribute("for"); //$NON-NLS-1$ if (replacementClasses.containsKey(replacementFor)) { throw new IllegalArgumentException( "Layer replacement class already exists for: " + replacementFor); //$NON-NLS-1$ } @SuppressWarnings("unchecked") Class<? extends IPersistentLayer> layer = (Class<? extends IPersistentLayer>) ExtensionPointHelper.getClassForProperty(element, "class"); //$NON-NLS-1$ replacementClasses.put(replacementFor, layer); } else if ("replacementId".equals(element.getName())) //$NON-NLS-1$ { String replacementFor = element.getAttribute("for"); //$NON-NLS-1$ if (replacementIds.containsKey(replacementFor)) { throw new IllegalArgumentException("Layer replacement id already exists for: " + replacementFor); //$NON-NLS-1$ } String replacementId = element.getAttribute("id"); //$NON-NLS-1$ replacementIds.put(replacementFor, replacementId); } else if ("drawOrder".equals(element.getName())) //$NON-NLS-1$ { @SuppressWarnings("unchecked") Class<? extends Layer> layer = (Class<? extends Layer>) ExtensionPointHelper.getClassForProperty(element, "class"); //$NON-NLS-1$ String valueString = element.getAttribute("value"); //$NON-NLS-1$ int value = Integer.parseInt(valueString); drawOrders.put(layer, value); } } catch (Exception e) { logger.error("Error processing layer", e); //$NON-NLS-1$ } } } /** * Get the id to use in the XML when persisting the given layer or wrapper. * * @param layerOrWrapper * @return Id for the given layer or wrapper */ public String getIdForLayerOrWrapper(IPersistentLayer layerOrWrapper) { String id = layerToId.get(layerOrWrapper.getClass()); if (id == null) { id = wrapperToId.get(layerOrWrapper.getClass()); } return id; } /** * Get the layer or wrapper class for the given id. Called when unpersisting * a layer/wrapper from XML. * * @param id * @return Layer or wrapper class associated with the given id */ public Class<? extends IPersistentLayer> getLayerOrWrapperForId(String id) { Class<? extends IPersistentLayer> layer = idToLayer.get(id); if (layer == null) { layer = idToWrapper.get(id); } return layer; } /** * Get the layer or wrapper class to use as a replacement for the given * layer/wrapper class name. This is useful if a layer/wrapper is replaced * with a new implementation, or moved to a different package. * * @param oldClassName * @return Replacement layer/wrapper class for the given class name */ public Class<? extends IPersistentLayer> getReplacementClassFor(String oldClassName) { return replacementClasses.get(oldClassName); } /** * Get the layer or wrapper id to use as a replacement for the given * layer/wrapper id. This is useful if a layer/wrapper is replaced with a * new implementation. * * @param oldId * @return Replacement layer/wrapper id for the given id */ public String getReplacementIdFor(String oldId) { return replacementIds.get(oldId); } /** * Get the default draw order value for the given layer class. * * @param layerClass * @return Default draw order value for the given layer class */ public Integer getDrawOrderFor(Class<? extends Layer> layerClass) { Map<Class<?>, Integer> distances = new HashMap<Class<?>, Integer>(); calculateClassDistances(layerClass, 0, distances, drawOrders); Class<?> closestClass = getClosestClass(distances); if (closestClass != null) { return drawOrders.get(closestClass); } return null; } /** * Wrap the given legacy World Wind {@link Layer} in the appropriate * associated {@link ILayerWrapper} implementation. * * @param layer * Layer to wrap * @return {@link ILayerWrapper} wrapping the given layer */ public ILayerWrapper wrapLayer(Layer layer) { try { ILayerWrapper wrapper = getWrapperForLayer(layer); wrapper.setLayer(layer); return wrapper; } catch (Exception e) { logger.error("Error wrapping layer", e); //$NON-NLS-1$ return null; } } private ILayerWrapper getWrapperForLayer(Layer layer) throws InstantiationException, IllegalAccessException { Class<?> layerClass = layer.getClass(); Map<Class<?>, Integer> distances = new HashMap<Class<?>, Integer>(); calculateClassDistances(layerClass, 0, distances, layerToWrapper); while (!distances.isEmpty()) { Class<?> closestClass = getClosestClass(distances); Class<? extends ILayerWrapper> wrapperClass = layerToWrapper.get(closestClass); ILayerWrapper wrapper = wrapperClass.newInstance(); if (wrapper.supports(layer)) { return wrapper; } distances.remove(closestClass); } return null; } private static Class<?> getClosestClass(Map<Class<?>, Integer> distances) { int minDistance = Integer.MAX_VALUE; Class<?> closestClass = null; for (Entry<Class<?>, Integer> entry : distances.entrySet()) { if (entry.getValue() < minDistance) { minDistance = entry.getValue(); closestClass = entry.getKey(); } } return closestClass; } private static void calculateClassDistances(Class<?> c, int distance, Map<Class<?>, Integer> distances, Map<Class<? extends Layer>, ?> inMap) { if (c == null) { return; } if (inMap.containsKey(c)) { Integer lastDistance = distances.get(c); if (lastDistance == null || distance < lastDistance) { distances.put(c, distance); } } calculateClassDistances(c.getSuperclass(), distance + 1, distances, inMap); for (Class<?> i : c.getInterfaces()) { calculateClassDistances(i, distance + 1, distances, inMap); } } }