package org.esa.snap.rcp.layermanager; import com.bc.ceres.core.Assert; import com.bc.ceres.core.ExtensionFactory; import com.bc.ceres.core.ExtensionManager; import com.bc.ceres.core.SingleTypeExtensionFactory; import com.bc.ceres.glayer.Layer; import com.bc.ceres.glayer.LayerType; import org.esa.snap.ui.layer.DefaultLayerSourceDescriptor; import org.esa.snap.ui.layer.LayerEditor; import org.esa.snap.ui.layer.LayerSource; import org.esa.snap.ui.layer.LayerSourceDescriptor; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.modules.ModuleInfo; import org.openide.modules.OnStart; import org.openide.util.Lookup; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Either 'editor' or 'editorFactory' must be given: * <ul> * <li>'editor' must be a <code>LayerEditor</code></li> * <li>'editorFactory' must be a <code>com.bc.ceres.core.ExtensionFactory</code> that produces * instances of <code>LayerEditor</code>.</li> * </ul> * <pre> * <editor field="layerEditorClass" type="java.lang.Class"/> * <editorFactory field="layerEditorFactoryClass" type="java.lang.Class"/> * </pre> * At least 'layer' or 'layerType' must be given: * <ul> * <li>'layer' must be a <code>com.bc.ceres.glayer.Layer</code></li> * <li>'layerType' must be a <code>com.bc.ceres.glayer.LayerType</code>.</li> * </ul> * <pre> * <layer field="layerClass" type="java.lang.Class"/> * <layerType field="layerTypeClass" type="java.lang.Class"/> * </pre> * * @author Norman Fomferra */ public class LayerManager { public static final Logger LOG = Logger.getLogger(LayerManager.class.getName()); private static LayerManager layerManager; private final Map<String, LayerSourceDescriptor> layerSourceDescriptors; public static LayerManager getDefault() { if (layerManager == null) { layerManager = new LayerManager(); } return layerManager; } public Map<String, LayerSourceDescriptor> getLayerSourceDescriptors() { return Collections.unmodifiableMap(layerSourceDescriptors); } protected LayerManager() { registerLayerEditors(); this.layerSourceDescriptors = lookupLayerSourceDescriptors(); } private static Map<String, LayerSourceDescriptor> lookupLayerSourceDescriptors() { Map<String, LayerSourceDescriptor> layerSourceDescriptors = new LinkedHashMap<>(); FileObject[] files = FileUtil.getConfigFile("LayerSources").getChildren(); //System.out.println("Files in SNAP/LayerSources: " + Arrays.toString(files)); List<FileObject> orderedFiles = FileUtil.getOrder(Arrays.asList(files), true); for (FileObject file : orderedFiles) { LayerSourceDescriptor layerSourceDescriptor = null; try { layerSourceDescriptor = createLayerSourceDescriptor(file); } catch (Exception e) { LOG.log(Level.SEVERE, String.format("Failed to create layer source from layer.xml path '%s'", file.getPath()), e); } if (layerSourceDescriptor != null) { layerSourceDescriptors.put(layerSourceDescriptor.getId(), layerSourceDescriptor); LOG.info(String.format("New layer source added from layer.xml path '%s': %s", file.getPath(), layerSourceDescriptor.getName())); } } return layerSourceDescriptors; } public static DefaultLayerSourceDescriptor createLayerSourceDescriptor(FileObject fileObject) { String id = fileObject.getName(); String name = (String) fileObject.getAttribute("displayName"); String description = (String) fileObject.getAttribute("description"); Class<? extends LayerSource> layerSourceClass = getClassAttribute(fileObject, "layerSourceClass", LayerSource.class, false); String layerTypeClassName = (String) fileObject.getAttribute("layerTypeClass"); Assert.argument(name != null && !name.isEmpty(), "Missing attribute 'displayName'"); Assert.argument(layerSourceClass != null || layerTypeClassName != null, "Either attribute 'class' or 'layerType' must be provided"); if (layerSourceClass != null) { return new DefaultLayerSourceDescriptor(id, name, description, layerSourceClass); } else { return new DefaultLayerSourceDescriptor(id, name, description, layerTypeClassName); } } private static void registerLayerEditors() { FileObject[] files = FileUtil.getConfigFile("LayerEditors").getChildren(); //System.out.println("Files in SNAP/LayerEditors: " + Arrays.toString(files)); List<FileObject> orderedFiles = FileUtil.getOrder(Arrays.asList(files), true); for (FileObject file : orderedFiles) { try { registerLayerEditorDescriptor(file); LOG.info(String.format("New layer editor registered from layer.xml path '%s'", file.getPath())); } catch (Exception e) { LOG.log(Level.SEVERE, String.format("Failed to register layer editor from layer.xml path '%s'", file.getPath()), e); } } } public static void registerLayerEditorDescriptor(FileObject fileObject) throws Exception { Class<? extends LayerEditor> editorClass = getClassAttribute(fileObject, "editorClass", LayerEditor.class, false); Class<? extends ExtensionFactory> editorFactoryClass = getClassAttribute(fileObject, "editorFactoryClass", ExtensionFactory.class, false); Assert.argument(editorClass != null || editorFactoryClass != null, "Either 'editorClass' or 'editorFactoryClass' attributes must be given"); Class<? extends Layer> layerClass = getClassAttribute(fileObject, "layerClass", Layer.class, false); Class<? extends LayerType> layerTypeClass = getClassAttribute(fileObject, "layerTypeClass", LayerType.class, false); Assert.argument(layerClass != null || layerTypeClass != null, "Either 'layerClass' or 'layerTypeClass' attributes must be given"); if (layerClass != null) { ExtensionManager.getInstance().register(layerClass, createExtensionFactory(editorClass, editorFactoryClass)); } else { ExtensionManager.getInstance().register(layerTypeClass, createExtensionFactory(editorClass, editorFactoryClass)); } } public static <T> Class<T> getClassAttribute(FileObject fileObject, String attributeName, Class<T> expectedType, boolean required) { String className = (String) fileObject.getAttribute(attributeName); if (className == null || className.isEmpty()) { if (required) { throw new IllegalArgumentException(String.format("Missing attribute '%s' of type %s", attributeName, expectedType.getName())); } return null; } Collection<? extends ModuleInfo> modules = Lookup.getDefault().lookupAll(ModuleInfo.class); for (ModuleInfo module : modules) { if (module.isEnabled()) { try { Class<?> implClass = module.getClassLoader().loadClass(className); if (expectedType.isAssignableFrom(implClass)) { //noinspection unchecked return (Class<T>) implClass; } else { throw new IllegalArgumentException(String.format("Value %s of attribute '%s' must be a %s", implClass.getName(), attributeName, expectedType.getName())); } } catch (ClassNotFoundException e) { // it's ok, continue } } } return null; } /** * Creates an extension factory that maps an instances of a {@link com.bc.ceres.glayer.Layer} or * a {@link com.bc.ceres.glayer.LayerType} to an instance of a {@link LayerEditor}. */ private static ExtensionFactory createExtensionFactory(Class<? extends LayerEditor> editorClass, Class<? extends ExtensionFactory> editorFactoryClass) throws Exception { if (editorClass != null) { return new SingleTypeExtensionFactory<LayerType, LayerEditor>(LayerEditor.class, editorClass); } if (editorFactoryClass != null) { try { return editorFactoryClass.newInstance(); } catch (Exception e) { throw new IllegalStateException(e); } } // should never get here throw new IllegalStateException(); } @SuppressWarnings("UnusedDeclaration") @OnStart public static class Runner implements Runnable { @Override public void run() { // test! LayerManager.getDefault(); } } }