/*******************************************************************************
* 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.wrappers;
import gov.nasa.worldwind.Factory;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.avlist.AVListImpl;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.util.WWXML;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.xpath.XPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import au.gov.ga.earthsci.common.util.XmlUtil;
import au.gov.ga.earthsci.layer.LayerFactory;
import au.gov.ga.earthsci.layer.delegator.LayerDelegator;
import au.gov.ga.earthsci.layer.intent.IntentLayerLoader;
import au.gov.ga.earthsci.layer.tree.ILayerNode;
import au.gov.ga.earthsci.worldwind.common.util.AVKeyMore;
/**
* Basic {@link ILayerWrapper} implementation for any {@link Layer} created from
* an XML layer definition, or from a {@link URI} using the
* {@link IntentLayerLoader}.
* <p/>
* If the layer from created by the {@link LayerFactory} from an XML element,
* the factory saves the XML element used to create the layer as part of the
* layer's value map ({@link AVList}). This wrapper pulls out the element, and
* saves it when persisting the layer. When loading the layer, it uses the same
* XML definition to load the layer.
* <p/>
* Subclasses can edit elements/attributes within the XML element during the
* editing process; this is to support editing legacy layers.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class DefaultLayerWrapper extends LayerDelegator implements ILayerWrapper
{
private static final Logger logger = LoggerFactory.getLogger(DefaultLayerWrapper.class);
public final static String CLASS_ELEMENT = "class"; //$NON-NLS-1$
public final static String BUNDLE_ATTRIBUTE = "bundle"; //$NON-NLS-1$
public final static String NAME_ATTRIBUTE = "name"; //$NON-NLS-1$
public final static String LEGACY_ELEMENT = "legacy"; //$NON-NLS-1$
public final static String URI_ELEMENT = "uri"; //$NON-NLS-1$
public final static String URL_ELEMENT = "url"; //$NON-NLS-1$
protected String classBundle;
protected String className;
protected Element element;
protected URI uri;
protected URL url;
private boolean reloadingLayer;
@Override
public boolean supports(Layer layer)
{
//supports all Layer implementations:
return true;
}
@Override
public void setLayer(Layer layer)
{
super.setLayer(layer);
if (reloadingLayer)
{
return;
}
classBundle = FrameworkUtil.getBundle(layer.getClass()).getSymbolicName();
className = layer.getClass().getName();
element = (Element) layer.getValue(LayerFactory.LAYER_ELEMENT);
uri = (URI) layer.getValue(IntentLayerLoader.LAYER_URI_KEY);
url = (URL) layer.getValue(AVKeyMore.CONTEXT_URL);
if (url == null)
{
AVList params = (AVList) layer.getValue(AVKey.CONSTRUCTION_PARAMETERS);
if (params != null)
{
url = (URL) params.getValue(AVKeyMore.CONTEXT_URL);
}
}
}
@Override
public boolean isLoading()
{
return reloadingLayer;
}
@Override
public void load(Element parent)
{
XPath xpath = WWXML.makeXPath();
element = null;
uri = null;
url = null;
classBundle = XmlUtil.getText(parent, CLASS_ELEMENT + "/@" + BUNDLE_ATTRIBUTE, null, xpath); //$NON-NLS-1$
className = XmlUtil.getText(parent, CLASS_ELEMENT + "/@" + NAME_ATTRIBUTE, null, xpath); //$NON-NLS-1$
Element definitionElement = WWXML.getElement(parent, LEGACY_ELEMENT, xpath);
if (definitionElement != null)
{
element = XmlUtil.getFirstChildElement(definitionElement);
}
String uriText = XmlUtil.getText(parent, URI_ELEMENT, null, xpath);
if (uriText != null)
{
try
{
uri = new URI(uriText);
}
catch (URISyntaxException e)
{
logger.error("Error converting text to URI: " + uriText); //$NON-NLS-1$
}
}
String urlText = XmlUtil.getText(parent, URL_ELEMENT, null, xpath);
if (urlText != null)
{
try
{
url = new URL(urlText);
}
catch (MalformedURLException e)
{
logger.error("Error converting text to URL: " + urlText); //$NON-NLS-1$
}
}
}
@Override
public void save(Element parent)
{
if (uri != null)
{
XmlUtil.setTextElement(parent, URI_ELEMENT, uri.toString());
}
if (element != null)
{
Element legacyElement = parent.getOwnerDocument().createElement(LEGACY_ELEMENT);
parent.appendChild(legacyElement);
Node imported = legacyElement.getOwnerDocument().importNode(element, true);
legacyElement.appendChild(imported);
if (url != null)
{
XmlUtil.setTextElement(parent, URL_ELEMENT, url.toString());
}
}
else if (className != null && classBundle != null && uri == null)
{
Element element = XmlUtil.createElement(parent, CLASS_ELEMENT, null);
element.setAttribute(BUNDLE_ATTRIBUTE, classBundle);
element.setAttribute(NAME_ATTRIBUTE, className);
}
}
@Override
public void initialize(ILayerNode node, IEclipseContext context)
{
try
{
reloadingLayer = true;
if (element != null)
{
reloadFromElement();
}
else if (uri != null)
{
reloadFromUri(node, context);
}
else if (classBundle != null && className != null)
{
reloadFromClassName();
}
else
{
logger.error("Error loading wrapped layer, no definition/url/class defined"); //$NON-NLS-1$
}
}
finally
{
reloadingLayer = false;
}
}
/**
* Reload this layer from the XML element.
*/
protected void reloadFromElement()
{
URL url = this.url;
if (uri != null)
{
try
{
url = uri.toURL();
}
catch (MalformedURLException e)
{
//ignore;
}
}
AVList params = new AVListImpl();
params.setValue(AVKeyMore.CONTEXT_URL, url);
Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.LAYER_FACTORY);
try
{
Object result = factory.createFromConfigSource(element, params);
if (!(result instanceof Layer))
{
if (result == null)
{
logger.error("Error loading layer from element, layer factory returned null"); //$NON-NLS-1$
}
else
{
logger.error("Object loaded is not a Layer: " + result.getClass()); //$NON-NLS-1$
}
return;
}
Layer layer = (Layer) result;
setLayer(layer);
}
catch (Exception e)
{
logger.error("Error loading layer from element", e); //$NON-NLS-1$
}
}
/**
* Reload this layer from the URI, using the {@link IntentLayerLoader}.
*
* @param node
* @param context
*/
protected void reloadFromUri(ILayerNode node, IEclipseContext context)
{
try
{
IntentLayerLoader.load(uri, node, context);
}
catch (Exception e)
{
logger.error("Error loading layer from URL: " + uri, e); //$NON-NLS-1$
}
}
/**
* Reload this layer from a class name.
*/
protected void reloadFromClassName()
{
try
{
Bundle bundle = Platform.getBundle(classBundle);
@SuppressWarnings("unchecked")
Class<? extends Layer> layerClass = (Class<? extends Layer>) bundle.loadClass(className);
Layer layer = layerClass.newInstance();
setLayer(layer);
}
catch (Exception e)
{
logger.error("Error instantiating layer class", e); //$NON-NLS-1$
}
}
}