/**
  * removes a Layer identified by its title from the parent Layer. A reference to the removed layer
  * will be returned. If no Layer matching the passed title can be found nothing happens and
  * <tt>null</tt> will be returned.
  *
  * @param title
  * @return removerd Layer
  */
 public Layer removeLayerByTitle(String title) {
   for (int i = 0; i < layer.size(); i++) {
     Layer ly = layer.get(i);
     if (ly.getTitle().equals(title)) {
       layer.remove(i);
       return ly;
     }
   }
   return null;
 }
 /**
  * removes a Layer identified by its name from the parent Layer. A reference to the removed layer
  * will be returned. If no Layer matching the passed name can be found nothing happens and
  * <tt>null</tt> will be returned.
  *
  * @param name
  * @return removerd Layer
  */
 public Layer removeLayer(String name) {
   for (int i = 0; i < layer.size(); i++) {
     Layer ly = layer.get(i);
     if (ly.getName() != null) {
       if (ly.getName().equals(name)) {
         layer.remove(i);
         return ly;
       }
     }
   }
   return null;
 }
  /**
   * returns the <tt>UserStyle</tt> (SLD) representation of the style identified by the submitted
   * name.
   *
   * @param name of the requested style
   * @return SLD - UserStyle
   */
  public UserStyle getStyle(String name) {

    Style style = styles.get(name);
    UserStyle us = null;

    if (style == null) {
      if (parent != null) {
        us = parent.getStyle(name);
      }
    } else {
      us = style.getStyleContent();
    }

    return us;
  }
  /**
   * Extent declarations are inherited from parent Layers. Any Extent declarations in the child with
   * the same name attribute as one inherited from the parent replaces the value declared by the
   * parent. A Layer shall not declare an Extent unless a Dimension with the same name has been
   * declared or inherited earlier in the Capabilities XML.
   *
   * @return the extents
   */
  public Extent[] getExtent() {
    HashMap<String, Extent> list = new HashMap<String, Extent>();

    if (parent != null) {
      Extent[] pEx = parent.getExtent();

      for (int i = 0; i < pEx.length; i++) {
        list.put(pEx[i].getName(), pEx[i]);
      }
    }

    for (int i = 0; i < extent.size(); i++) {
      Extent ex = extent.get(i);
      list.put(ex.getName(), ex);
    }

    return list.values().toArray(new Extent[list.size()]);
  }
  /** @return a list of style that can be used form rendering the layer. */
  public Style[] getStyles() {
    HashMap<String, Style> list = new HashMap<String, Style>();

    if (parent != null) {
      Style[] pStyle = parent.getStyles();

      for (int i = 0; i < pStyle.length; i++) {
        list.put(pStyle[i].getName(), pStyle[i]);
      }
    }

    for (Style style : styles.values()) {
      if (list.get(style.getName()) == null) {
        list.put(style.getName(), style);
      }
    }

    return list.values().toArray(new Style[list.size()]);
  }
  /**
   * Layers may have zero or more &lt;BoundingBox&gt; elements that are either stated explicitly or
   * inherited from a parent Layer. Each BoundingBox states the bounding rectangle of the map data
   * in a particular spatial reference system; the attribute SRS indicates which SRS applies. If the
   * data area is shaped irregularly then the BoundingBox gives the minimum enclosing rectangle. The
   * attributes minx, miny, maxx, maxy indicate the edges of the bounding box in units of the
   * specified SRS. Optional resx and resy attributes indicate the spatial resolution of the data in
   * those same units.
   *
   * <p>A Layer may have multiple BoundingBox element, but each one shall state a different SRS. A
   * Layer inherits any BoundingBox values defined by its parents. A BoundingBox inherited from the
   * parent Layer for a particular SRS is replaced by any declaration for the same SRS in the child
   * Layer. A BoundingBox in the child for a new SRS not already declared by the parent is added to
   * the list of bounding boxes for the child Layer. A single Layer element shall not contain more
   * than one BoundingBox for the same SRS.
   *
   * @return bounding boxes
   */
  public LayerBoundingBox[] getBoundingBoxes() {
    HashMap<String, LayerBoundingBox> list = new HashMap<String, LayerBoundingBox>(100);

    if (parent != null) {
      LayerBoundingBox[] plb = parent.getBoundingBoxes();

      for (int i = 0; i < plb.length; i++) {
        list.put(plb[i].getSRS(), plb[i]);
      }
    }

    for (int i = 0; i < boundingBox.size(); i++) {
      LayerBoundingBox lb = (LayerBoundingBox) boundingBox.get(i);
      list.put(lb.getSRS(), lb);
    }

    LayerBoundingBox[] lbs = new LayerBoundingBox[list.size()];
    return list.values().toArray(lbs);
  }
  /**
   * Every Layer is available in one or more spatial reference systems Every Layer shall have at
   * least one &gt;SRS&gt; element that is either stated explicitly or inherited from a parent Layer
   * . The root &lt;Layer&gt; element shall include a sequence of zero or more SRS elements listing
   * all SRSes that are common to all subsidiary layers. Use a single SRS element with empty content
   * (like so: "&lt;SRS&gt;&lt;/SRS&gt; ") if there is no common SRS. Layers may optionally add to
   * the global SRS list, or to the list inherited from a parent layer. Any duplication shall be
   * ignored by clients. When a Layer is available in several Spatial Reference Systems, there are
   * two ways to encode the list of SRS values. The first of these is new in this version of the
   * specification, the second is deprecated but still included for backwards compatibility.
   *
   * <p>1. Optional, recommended: Multiple single-valued &lt;SRS&gt; elements: a list of SRS values
   * is represented as a sequence of &lt;SRS&gt; elements, each of which contains only a single SRS
   * name. Example: &lt;SRS&gt;EPSG:1234&lt;/SRS&gt; &lt;SRS&gt;EPSG:5678&lt;/SRS&gt;. 2.
   * Deprecated: Single list-valued &lt;SRS&gt; element: a list of SRS values is represented asa
   * whitespace-separated list of SRS names contained within a single &lt;SRS&gt; element. Example:
   * &lt;SRS&gt;EPSG:1234 EPSG:5678&lt;/SRS&gt;.
   *
   * @return the srs
   */
  public String[] getSrs() {
    String[] pSrs = null;

    if (parent != null) {
      pSrs = parent.getSrs();
    } else {
      pSrs = new String[0];
    }

    List<String> list = new ArrayList<String>(srs.size() + pSrs.length);
    list.addAll(srs);
    for (int i = 0; i < pSrs.length; i++) {
      if (!list.contains(pSrs[i])) {
        list.add(pSrs[i]);
      }
    }

    return list.toArray(new String[list.size()]);
  }
  /**
   * Dimension declarations are inherited from parent Layers. Any new Dimension declarations in the
   * child are added to the list inherited from the parent. A child shall not redefine a Dimension
   * with the same name attribute as one that was inherited.
   *
   * @return the dimensions
   */
  public Dimension[] getDimension() {
    HashMap<String, Dimension> list = new HashMap<String, Dimension>();

    if (parent != null) {
      Dimension[] pDim = parent.getDimension();

      for (int i = 0; i < pDim.length; i++) {
        list.put(pDim[i].getName(), pDim[i]);
      }
    }

    for (int i = 0; i < dimension.size(); i++) {
      Dimension dim = dimension.get(i);

      if (list.get(dim.getName()) == null) {
        list.put(dim.getName(), dim);
      }
    }

    return list.values().toArray(new Dimension[list.size()]);
  }
  /**
   * A Map Server may use zero or more &lt;Identifier&gt; elements to list ID numbers or labels
   * defined by a particular Authority. The text content of the Identifier element is the ID value.
   *
   * @return the identifiers
   */
  public Identifier[] getIdentifier() {
    HashMap<String, Identifier> list = new HashMap<String, Identifier>();

    if (parent != null) {
      Identifier[] pIden = parent.getIdentifier();

      for (int i = 0; i < pIden.length; i++) {
        list.put(pIden[i].getAuthority(), pIden[i]);
      }
    }

    for (int i = 0; i < identifier.size(); i++) {
      Identifier iden = identifier.get(i);

      if (list.get(iden.getAuthority()) == null) {
        list.put(iden.getAuthority(), iden);
      }
    }

    Identifier[] ids = new Identifier[list.size()];
    return list.values().toArray(ids);
  }
  /**
   * The authority attribute of the Identifier element corresponds to the name attribute of a
   * separate &lt;AuthorityURL&gt; element. AuthorityURL encloses an &lt;OnlineResource&gt; element
   * which states the URL of a document defining the meaning of the Identifier values.
   *
   * @return the authority url object
   */
  public AuthorityURL[] getAuthorityURL() {
    HashMap<String, AuthorityURL> list = new HashMap<String, AuthorityURL>();

    if (parent != null) {
      AuthorityURL[] pAu = parent.getAuthorityURL();

      for (int i = 0; i < pAu.length; i++) {
        list.put(pAu[i].getName(), pAu[i]);
      }
    }

    for (int i = 0; i < authorityURL.size(); i++) {
      AuthorityURL au = authorityURL.get(i);

      if (list.get(au.getName()) == null) {
        list.put(au.getName(), au);
      }
    }

    AuthorityURL[] aus = new AuthorityURL[list.size()];
    return list.values().toArray(aus);
  }
  /**
   * returns the <tt>Style</tt> identified by the submitted name.
   *
   * @param name of the requested style
   * @return Style
   */
  public Style getStyleResource(String name) {

    Style style = styles.get(name);

    if (style == null && name.length() == 0) {
      String tmpName = "default";
      style = styles.get(tmpName);
      if (style == null && name.length() == 0) {
        tmpName = "default:" + this.name;
        style = styles.get(tmpName);
      }
    } else if (style == null && "default".equals(name)) {
      String tmpName = "default:" + this.name;
      style = styles.get(tmpName);
    }

    if (style == null) {
      if (parent != null) {
        style = parent.getStyleResource(name);
      }
    }

    return style;
  }
  /**
   * appends the selected layers of a WMS to the passed <code>ViewContext</code>
   *
   * @param context
   * @throws ContextException
   * @throws MalformedURLException
   * @throws PortalException
   * @throws InvalidCapabilitiesException
   */
  private void appendWMS(RPCWebEvent rpc, ViewContext context)
      throws MalformedURLException, ContextException, PortalException,
          InvalidCapabilitiesException {

    RPCStruct struct = (RPCStruct) rpc.getRPCMethodCall().getParameters()[0].getValue();
    URL url = new URL((String) struct.getMember("WMSURL").getValue());
    String name = (String) struct.getMember("WMSNAME").getValue();
    String version = (String) struct.getMember("WMSVERSION").getValue();
    String layers = (String) struct.getMember("LAYERS").getValue();
    String formatName = (String) struct.getMember("FORMAT").getValue();
    boolean useAuthentification = false;
    if (struct.getMember("useAuthentification") != null) {
      String tmp = (String) struct.getMember("useAuthentification").getValue();
      useAuthentification = "true".equalsIgnoreCase(tmp);
    }

    List<String> list = StringTools.toList(layers, ";", true);

    WMSCapabilitiesDocument capa = null;
    try {
      StringBuffer sb = new StringBuffer(500);
      if ("1.0.0".equals(version)) {
        sb.append(url.toExternalForm()).append("?request=capabilities&service=WMS");
      } else {
        sb.append(url.toExternalForm()).append("?request=GetCapabilities&service=WMS");
      }
      if (useAuthentification) {
        HttpSession session = ((HttpServletRequest) getRequest()).getSession();
        String user =
            ((org.apache.jetspeed.om.security.BaseJetspeedUser)
                    session.getAttribute("turbine.user"))
                .getUserName();
        String password =
            ((org.apache.jetspeed.om.security.BaseJetspeedUser)
                    session.getAttribute("turbine.user"))
                .getPassword();
        if (!"anon".equals(user)) {
          sb.append("&user="******"&password="******"GetCapabilites for added WMS", sb.toString());
      capa = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument(new URL(sb.toString()));
    } catch (Exception e) {
      LOG.logError(e.getMessage(), e);
      String msg = null;
      if ("1.0.0".equals(version)) {
        msg =
            StringTools.concat(
                500,
                "could not load WMS capabilities from: ",
                new URL(url.toExternalForm() + "?request=capabilities&service=WMS"),
                "; reason: ",
                e.getMessage());
      } else {
        msg =
            StringTools.concat(
                500,
                "could not load WMS capabilities from: ",
                new URL(url.toExternalForm() + "?request=GetCapabilities&service=WMS"),
                "; reason: ",
                e.getMessage());
      }
      throw new PortalException(msg);
    }
    WMSCapabilities capabilities = (WMSCapabilities) capa.parseCapabilities();
    String rootTitle = capabilities.getLayer().getTitle();

    // ----------------------------------------------------------------------------
    // stuff required by layerlist tree view
    Node root = context.getGeneral().getExtension().getLayerTreeRoot();
    // check if Node width this title already exists
    Node[] nodes = root.getNodes();
    int newNodeId = -1;
    for (int j = 0; j < nodes.length; j++) {
      if (nodes[j].getTitle().equals(rootTitle)) {
        newNodeId = nodes[j].getId();
        break;
      }
    }
    if (newNodeId == -1) {
      newNodeId = root.getMaxNodeId() + 1;
      Node newNode = new Node(newNodeId, root, rootTitle, true, false);
      Node[] newNodes = new Node[nodes.length + 1];
      newNodes[0] = newNode;
      for (int j = 0; j < nodes.length; j++) {
        newNodes[j + 1] = nodes[j];
      }

      root.setNodes(newNodes);
    }
    // ----------------------------------------------------------------------------
    for (int i = 0; i < list.size(); i++) {
      String[] lay = StringTools.toArray(list.get(i), "|", false);
      Server server = new Server(name, version, "OGC:WMS", url, capabilities);
      String srs = context.getGeneral().getBoundingBox()[0].getCoordinateSystem().getPrefixedName();
      Format format = new Format(formatName, true);
      FormatList fl = new FormatList(new Format[] {format});
      // read available styles from WMS capabilities and add them
      // to the WMC layer
      org.deegree.ogcwebservices.wms.capabilities.Layer wmslay = capabilities.getLayer(lay[0]);
      org.deegree.ogcwebservices.wms.capabilities.Style[] wmsstyles = wmslay.getStyles();
      Style[] styles = null;
      if (wmsstyles == null || wmsstyles.length == 0) {
        // a wms capabilities layer may offeres one or more styles for
        // a layer but it don't have to. But WMC must have at least one
        // style for each layer; So we set a default style in the case
        // a wms does not declares one
        styles = new Style[1];
        styles[0] = new Style("", "default", "", null, true);
      } else {
        styles = new Style[wmsstyles.length];
        for (int j = 0; j < styles.length; j++) {
          boolean isDefault =
              wmsstyles[j].getName().toLowerCase().indexOf("default") > -1
                  || wmsstyles[j].getName().trim().length() == 0;
          ImageURL legendURL = null;
          LegendURL[] lUrl = wmsstyles[j].getLegendURL();
          if (lUrl != null && lUrl.length > 0) {
            legendURL =
                new ImageURL(
                    lUrl[0].getWidth(),
                    lUrl[0].getHeight(),
                    lUrl[0].getFormat(),
                    lUrl[0].getOnlineResource());
          }
          styles[j] =
              new Style(
                  wmsstyles[j].getName(),
                  wmsstyles[j].getTitle(),
                  wmsstyles[j].getAbstract(),
                  legendURL,
                  isDefault);
        }
      }

      StyleList styleList = new StyleList(styles);
      BaseURL mdUrl = null;
      MetadataURL[] mdUrls = wmslay.getMetadataURL();
      if (mdUrls != null && mdUrls.length == 1 && mdUrls[0] != null) {
        mdUrl = mdUrls[0];
      }

      int authentication = LayerExtension.NONE;
      if (useAuthentification) {
        authentication = LayerExtension.USERPASSWORD;
      }
      LayerExtension lex =
          new LayerExtension(
              null,
              false,
              wmslay.getScaleHint().getMin(),
              wmslay.getScaleHint().getMax(),
              false,
              authentication,
              newNodeId,
              false,
              null);
      Layer newLay =
          new Layer(
              server,
              lay[0],
              lay[1],
              null,
              new String[] {srs},
              null,
              mdUrl,
              fl,
              styleList,
              wmslay.isQueryable(),
              false,
              lex);
      if (context
              .getLayerList()
              .getLayer(newLay.getName(), server.getOnlineResource().toExternalForm())
          == null) {
        context.getLayerList().addLayerToTop(newLay);
      }
    }
    try {
      XMLFragment xml = XMLFactory.export(context);
      System.out.println(xml.getAsPrettyString());
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
 /**
  * Layers may include a &lt;ScaleHint&gt; element that suggests minimum and maximum scales for
  * which it is appropriate to display this layer. Because WMS output is destined for output
  * devices of arbitrary size and resolution, the usual definition of scale as the ratio of map
  * size to real-world size is not appropriate here. The following definition of Scale Hint is
  * recommended. Consider a hypothetical map with a given Bounding Box, width and height. The
  * central pixel of that map (or the pixel just to the northwest of center) will have some size,
  * which can be expressed as the ground distance in meters of the southwest to northeast diagonal
  * of that pixel. The two values in ScaleHint are the minimum and maximum recommended values of
  * that diagonal. It is recognized that this definition is not geodetically precise, but at the
  * same time the hope is that by including it conventions will develop that can be later specified
  * more clearly.
  *
  * @return the scale hint
  */
 public ScaleHint getScaleHint() {
   if ((parent != null) && (scaleHint == null)) {
     return parent.getScaleHint();
   }
   return scaleHint;
 }
 /**
  * The optional &lt;Attribution&gt; element provides a way to identify the source of the map data
  * used in a Layer or collection of Layers. Attribution encloses several optional elements:
  * <OnlineResource>states the data provider's URL; &lt;Title&gt; is a human-readable string naming
  * the data provider; &lt;LogoURL&gt; is the URL of a logo image. Client applications may choose
  * to display one or more of these items. A &lt;Format&gt; element in LogoURL indicates the MIME
  * type of the logo image, and the attributes width and height state the size of the image in
  * pixels.
  *
  * @return the attribution
  */
 public Attribution getAttribution() {
   if ((parent != null) && (attribution == null)) {
     return parent.getAttribution();
   }
   return attribution;
 }
 /**
  * Every Layer shall have exactly one &lt;LatLonBoundingBox&gt; element that is either stated
  * explicitly or inherited from a parent Layer. LatLonBoundingBox states the minimum bounding
  * rectangle of the map data in the EPSG:4326 geographic coordinate system. The LatLonBoundingBox
  * attributes minx, miny, maxx, maxy indicate the edges of an enclosing rectangle in decimal
  * degrees. LatLonBoundingBox shall be supplied regardless of what SRS the map server may support,
  * but it may be approximate if EPSG:4326 is not supported. Its purpose is to facilitate
  * geographic searches without requiring coordinate transformations by the search engine.
  *
  * @return the bbox
  */
 public Envelope getLatLonBoundingBox() {
   if ((latLonBoundingBox == null) && (parent != null)) {
     return parent.getLatLonBoundingBox();
   }
   return latLonBoundingBox;
 }
  /**
   * returns the layers offered by the WMPS
   *
   * @param layerElem
   * @param parent
   * @return Layer
   * @throws XMLParsingException
   * @throws UnknownCRSException
   */
  protected Layer parseLayers(Element layerElem, Layer parent)
      throws XMLParsingException, UnknownCRSException {
    LOG.entering();

    boolean queryable = XMLTools.getNodeAsBoolean(layerElem, "./@queryable", nsContext, false);

    int cascaded = XMLTools.getNodeAsInt(layerElem, "./@cascaded", nsContext, 0);
    boolean opaque = XMLTools.getNodeAsBoolean(layerElem, "./@opaque", nsContext, false);
    boolean noSubsets = XMLTools.getNodeAsBoolean(layerElem, "./@noSubsets", nsContext, false);
    int fixedWidth = XMLTools.getNodeAsInt(layerElem, "./@fixedWidth", nsContext, 0);
    int fixedHeight = XMLTools.getNodeAsInt(layerElem, "./@fixedHeight", nsContext, 0);
    String name = XMLTools.getNodeAsString(layerElem, "./Name", nsContext, null);
    String title = XMLTools.getRequiredNodeAsString(layerElem, "./Title", nsContext);
    String layerAbstract = XMLTools.getNodeAsString(layerElem, "./Abstract", nsContext, null);
    String[] keywords = XMLTools.getNodesAsStrings(layerElem, "./KeywordList/Keyword", nsContext);
    String[] srs = XMLTools.getNodesAsStrings(layerElem, "./SRS", nsContext);

    List nl = XMLTools.getNodes(layerElem, "./BoundingBox", nsContext);
    // TODO substitue with Envelope
    LayerBoundingBox[] bboxes = null;
    if (nl.size() == 0 && parent != null) {
      // inherit BoundingBoxes from parent layer
      bboxes = parent.getBoundingBoxes();
    } else {
      bboxes = parseLayerBoundingBoxes(nl);
    }

    Element llBox = (Element) XMLTools.getNode(layerElem, "./LatLonBoundingBox", nsContext);
    Envelope llBoundingBox = null;

    if (llBox == null && parent != null) {
      // inherit LatLonBoundingBox parent layer
      llBoundingBox = parent.getLatLonBoundingBox();
    } else if (llBox != null) {
      llBoundingBox = parseLatLonBoundingBox(llBox);
    } else {
      /** Default crs = EPSG:4326 */
      CoordinateSystem crs = CRSFactory.create("EPSG:4326");
      llBoundingBox = GeometryFactory.createEnvelope(-180, -90, 180, 90, crs);
    }

    Dimension[] dimensions = parseDimensions(layerElem);

    Extent[] extents = parseExtents(layerElem);

    Attribution attribution = parseAttribution(layerElem);

    AuthorityURL[] authorityURLs = parseAuthorityURLs(layerElem);

    MetadataURL[] metadataURLs = parseMetadataURLs(layerElem);

    DataURL[] dataURLs = parseDataURL(layerElem);

    Identifier[] identifiers = parseIdentifiers(layerElem);

    FeatureListURL[] featureListURLs = parseFeatureListURL(layerElem);

    Style[] styles = parseStyles(layerElem);

    ScaleHint scaleHint = parseScaleHint(layerElem);

    Layer layer =
        new Layer(
            queryable,
            cascaded,
            opaque,
            noSubsets,
            fixedWidth,
            fixedHeight,
            name,
            title,
            layerAbstract,
            llBoundingBox,
            attribution,
            scaleHint,
            keywords,
            srs,
            bboxes,
            dimensions,
            extents,
            authorityURLs,
            identifiers,
            metadataURLs,
            dataURLs,
            featureListURLs,
            styles,
            null,
            null,
            parent);

    // get Child layers
    nl = XMLTools.getNodes(layerElem, "./Layer", nsContext);
    Layer[] layers = new Layer[nl.size()];
    for (int i = 0; i < layers.length; i++) {
      layers[i] = parseLayers((Element) nl.get(i), layer);
    }

    // set child layers
    layer.setLayer(layers);

    LOG.exiting();
    return layer;
  }