/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.catalog;
import java.util.ArrayList;
import java.util.List;
import org.geotools.geometry.jts.ReferencedEnvelope;
/**
* A map in which the layers grouped together can be referenced as
* a regular layer.
*
* @author Justin Deoliveira, The Open Planning Project
*
*/
public interface LayerGroupInfo extends PublishedInfo {
/**
* Enumeration for mode of layer group.
*/
public enum Mode {
/**
* The layer group is seen as a single exposed layer with a name, does not actually contain the layers it's referencing
*/
SINGLE {
public String getName() {
return "Single";
}
public Integer getCode() {
return 0;
}
},
/**
* The layer group is seen as a single exposed layer with a name, but contains the layers it's referencing,
* thus hiding them from the caps document unless also shown in other tree mode layers
*/
OPAQUE_CONTAINER {
public String getName() {
return "Opaque Container";
}
public Integer getCode() {
// added last, but a cross in between SINGLE and NAMED semantically,
// so added in this position
return 4;
}
},
/**
* The layer group retains a Name in the layer tree, but also exposes its nested layers in the capabilities document.
*/
NAMED {
public String getName() {
return "Named Tree";
}
public Integer getCode() {
return 1;
}
},
/**
* The layer group is exposed in the tree, but does not have a Name element, showing structure but making it impossible to get all the layers at once.
*/
CONTAINER {
public String getName() {
return "Container Tree";
}
public Integer getCode() {
return 2;
}
},
/**
* A special mode created to manage the earth observation requirements.
*/
EO {
public String getName() {
return "Earth Observation Tree";
}
public Integer getCode() {
return 3;
}
};
public abstract String getName();
public abstract Integer getCode();
}
/**
* Layer group mode.
*/
Mode getMode();
/**
* Sets layer group mode.
*/
void setMode( Mode mode );
/**
* Get whether the layer group is forced to be not queryable and hence can not be subject of a GetFeatureInfo request.
* <p>
* In order to preserve current default behavior (A LayerGroup is queryable when at least a
* child layer is queryable), this flag allows explicitly indicate that it is not queryable
* independently how the child layers are configured.
* </p>
* <p>
* Default is {@code false}
* </p>
*/
boolean isQueryDisabled();
/**
* Set the layer group to be not queryable and hence can not be subject of a GetFeatureInfo request.
*/
void setQueryDisabled(boolean queryDisabled);
/**
* Returns a workspace or <code>null</code> if global.
*/
WorkspaceInfo getWorkspace();
/**
* Get root layer.
*/
LayerInfo getRootLayer();
/**
* Set root layer.
*/
void setRootLayer(LayerInfo rootLayer);
/**
* Get root layer style.
*/
StyleInfo getRootLayerStyle();
/**
* Set root layer style.
*/
void setRootLayerStyle(StyleInfo style);
/**
* The layers and layer groups in the group.
*/
List<PublishedInfo> getLayers();
/**
* The styles for the layers in the group.
* <p>
* This list is a 1-1 correspondence to {@link #getLayers()}.
* </p>
*/
List<StyleInfo> getStyles();
/**
*
*
*/
List<LayerInfo> layers();
/**
*
*
*
*/
List<StyleInfo> styles();
/**
* The bounds for the base map.
*/
ReferencedEnvelope getBounds();
/**
* Sets the bounds for the base map.
*/
void setBounds( ReferencedEnvelope bounds );
/**
* Sets the workspace.
*/
void setWorkspace(WorkspaceInfo workspace);
/**
* A collection of metadata links for the resource.
*
* @uml.property name="metadataLinks"
* @see MetadataLinkInfo
*/
List<MetadataLinkInfo> getMetadataLinks();
/**
* A way to compare two LayerGroupInfo instances that works around all the wrappers we have
* around (secured, decorating ecc) all changing some aspects of the bean and breaking
* usage of "common" equality). This method only uses getters to fetch the fields.
* Could have been build using EqualsBuilder and reflection, but would have been very slow
* and we do lots of these calls on large catalogs.
*
* @param lg
* @param obj
* @return
*/
public static boolean equals(LayerGroupInfo lg, Object obj) {
if (lg == obj)
return true;
if (obj == null)
return false;
if (!( obj instanceof LayerGroupInfo) )
return false;
LayerGroupInfo other = (LayerGroupInfo) obj;
if (lg.getBounds() == null) {
if (other.getBounds() != null)
return false;
} else if (!lg.getBounds().equals(other.getBounds()))
return false;
if (lg.getId() == null) {
if (other.getId() != null)
return false;
} else if (!lg.getId().equals(other.getId()))
return false;
if (lg.getLayers() == null) {
if (other.getLayers() != null)
return false;
} else if (!lg.getLayers().equals(other.getLayers()))
return false;
if (lg.getMetadata() == null) {
if (other.getMetadata() != null)
return false;
} else if (!lg.getMetadata().equals(other.getMetadata()))
return false;
if (lg.getName() == null) {
if (other.getName() != null)
return false;
} else if (!lg.getName().equals(other.getName()))
return false;
if (lg.getMode() == null) {
if (other.getMode() != null)
return false;
} else if (!lg.getMode().equals(other.getMode()))
return false;
if (lg.getTitle() == null) {
if (other.getTitle() != null) {
return false;
}
} else if (!lg.getTitle().equals(other.getTitle()))
return false;
if (lg.getAbstract() == null) {
if (other.getAbstract() != null) {
return false;
}
} else if (!lg.getAbstract().equals(other.getAbstract()))
return false;
if (lg.getWorkspace() == null) {
if (other.getWorkspace() != null)
return false;
} else if (!lg.getWorkspace().equals(other.getWorkspace()))
return false;
List<StyleInfo> styles = canonicalStyles(lg.getStyles(), lg.getLayers());
List<StyleInfo> otherStyles = canonicalStyles(other.getStyles(), other.getLayers());
if (styles == null) {
if (otherStyles != null)
return false;
} else if (!styles.equals(otherStyles))
return false;
if(lg.getAuthorityURLs() == null){
if (other.getAuthorityURLs() != null)
return false;
} else if (!lg.getAuthorityURLs().equals(other.getAuthorityURLs()))
return false;
if(lg.getIdentifiers() == null){
if (other.getIdentifiers() != null)
return false;
} else if (!lg.getIdentifiers().equals(other.getIdentifiers()))
return false;
if(lg.getRootLayer() == null){
if (other.getRootLayer() != null)
return false;
} else if (!lg.getRootLayer().equals(other.getRootLayer()))
return false;
if(lg.getRootLayerStyle()== null){
if (other.getRootLayerStyle() != null)
return false;
} else if (!lg.getRootLayerStyle().equals(other.getRootLayerStyle()))
return false;
if(lg.getAttribution() == null){
if (other.getAttribution() != null)
return false;
} else if (!lg.getAttribution().equals(other.getAttribution()))
return false;
if(lg.getMetadataLinks() == null){
if (other.getMetadataLinks() != null)
return false;
} else if (!lg.getMetadataLinks().equals(other.getMetadataLinks()))
return false;
if (!lg.isQueryDisabled() == other.isQueryDisabled())
return false;
return true;
}
/**
* Styles, especially when using defaults, can be represented in too many ways (null, list
* of nulls, and so on). This returns a single canonical representation for those cases,
* trying not to allocate new objects.
*
* @param styles
* @param layers
* @return
*/
static List<StyleInfo> canonicalStyles(List<StyleInfo> styles, List<PublishedInfo> layers) {
if(styles == null || styles.isEmpty()) {
return null;
}
boolean allNull = true;
for (StyleInfo s : styles) {
if(s != null) {
allNull = false;
break;
}
}
if (allNull) {
return null;
}
// at least one non null element, are they at least aligned with layers?
if(styles.size() == layers.size()) {
return styles;
}
// not aligned, build a new representation
List<StyleInfo> canonical = new ArrayList<>(layers.size());
for (int i = 0; i < layers.size(); i++) {
StyleInfo s = styles.size() > i ? styles.get(i) : null;
canonical.add(s);
}
return canonical;
}
/**
* A way to build a hash code based only on LayerGroupInfo instances that works around all the wrappers we have
* around (secured, decorating ecc) all changing some aspects of the bean and breaking
* usage o "common" equality). This method only uses getters to fetch the fields.
* Could have been build using HashCodeBuilder and reflection, but would have been very slow
* and we do lots of these calls on large catalogs.
*/
public static int hashCode(LayerGroupInfo lg) {
final int prime = 31;
int result = 1;
result = prime * result + ((lg.getBounds() == null) ? 0 : lg.getBounds().hashCode());
result = prime * result + ((lg.getId() == null) ? 0 : lg.getId().hashCode());
result = prime * result + ((lg.getLayers() == null) ? 0 : lg.getLayers().hashCode());
result = prime * result + ((lg.getMetadata()== null) ? 0 : lg.getMetadata().hashCode());
result = prime * result + ((lg.getName() == null) ? 0 : lg.getName().hashCode());
result = prime * result + ((lg.getMode() == null) ? 0 : lg.getMode().hashCode());
result = prime * result + ((lg.getTitle() == null) ? 0 : lg.getTitle().hashCode());
result = prime * result + ((lg.getAbstract() == null) ? 0 : lg.getAbstract().hashCode());
result = prime * result + ((lg.getWorkspace() == null) ? 0 : lg.getWorkspace().hashCode());
result = prime * result + ((lg.getStyles() == null) ? 0 : lg.getStyles().hashCode());
result = prime * result + ((lg.getRootLayer() == null) ? 0 : lg.getRootLayer().hashCode());
result = prime * result + ((lg.getRootLayerStyle() == null) ? 0 : lg.getRootLayerStyle().hashCode());
result = prime * result + ((lg.getAuthorityURLs() == null) ? 0 : lg.getAuthorityURLs().hashCode());
result = prime * result + ((lg.getIdentifiers() == null) ? 0 : lg.getIdentifiers().hashCode());
result = prime * result + ((lg.getAttribution() == null) ? 0 : lg.getAttribution().hashCode());
result = prime * result + ((lg.getMetadataLinks() == null) ? 0 : lg.getMetadataLinks().hashCode());
result = prime * result + Boolean.hashCode(lg.isQueryDisabled());
return result;
}
}