/**
     * Handles the encoding of the layers elements.
     *
     * <p>This method does a search over the SRS of all the layers to see if there are at least a
     * common one, as needed by the spec: "<i>The root Layer element shall include a sequence of
     * zero or more &lt;SRS&gt; 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."</i>
     *
     * <p>By the other hand, this search is also used to collecto the whole latlon bbox, as stated
     * by the spec: <i>"The bounding box metadata in Capabilities XML specify the minimum enclosing
     * rectangle for the layer as a whole."</i>
     *
     * @task TODO: manage this differently when we have the layer list of the WMS service decoupled
     *     from the feature types configured for the server instance. (This involves nested layers,
     *     gridcoverages, etc)
     */
    private void handleLayers() {
      start("Layer");

      final List<LayerInfo> layers;

      // filter the layers if a namespace filter has been set
      if (request.getNamespace() != null) {
        final List<LayerInfo> allLayers = wmsConfig.getLayers();
        layers = new ArrayList<LayerInfo>();

        String namespace = wmsConfig.getNamespaceByPrefix(request.getNamespace());
        for (LayerInfo layer : allLayers) {
          Name name = layer.getResource().getQualifiedName();
          if (name.getNamespaceURI().equals(namespace)) {
            layers.add(layer);
          }
        }
      } else {
        layers = wmsConfig.getLayers();
      }

      WMSInfo serviceInfo = wmsConfig.getServiceInfo();
      element("Title", serviceInfo.getTitle());
      element("Abstract", serviceInfo.getAbstract());

      List<String> srsList = serviceInfo.getSRS();
      Set<String> srs = new HashSet<String>();
      if (srsList != null) {
        srs.addAll(srsList);
      }
      handleRootCrsList(srs);

      handleRootBbox(layers);

      // now encode each layer individually
      LayerTree featuresLayerTree = new LayerTree(layers);
      handleLayerTree(featuresLayerTree);

      try {
        List<LayerGroupInfo> layerGroups = wmsConfig.getLayerGroups();
        handleLayerGroups(new ArrayList<LayerGroupInfo>(layerGroups));
      } catch (FactoryException e) {
        throw new RuntimeException("Can't obtain Envelope of Layer-Groups: " + e.getMessage(), e);
      } catch (TransformException e) {
        throw new RuntimeException("Can't obtain Envelope of Layer-Groups: " + e.getMessage(), e);
      }

      end("Layer");
    }
  /**
   * Retrieves the WMS's capabilities document.
   *
   * @param scaleHintUnitsPerDiaPixel true if the scalehint must be in units per diagonal of a pixel
   * @return Capabilities as {@link Document}
   */
  private Document findCapabilities(Boolean scaleHintUnitsPerDiaPixel) throws Exception {
    // set the Scalehint units per diagonal pixel setting.
    WMS wms = getWMS();
    WMSInfo info = wms.getServiceInfo();
    MetadataMap mm = info.getMetadata();
    mm.put(WMS.SCALEHINT_MAPUNITS_PIXEL, scaleHintUnitsPerDiaPixel);
    info.getGeoServer().save(info);

    GetCapabilitiesTransformer tr =
        new GetCapabilitiesTransformer(wms, BASE_URL, FORMATS, LEGEND_FORMAT, null);
    GetCapabilitiesRequest req = new GetCapabilitiesRequest();
    req.setBaseUrl(BASE_URL);
    req.setVersion(WMS.VERSION_1_1_1.toString());

    Document dom = WMSTestSupport.transform(req, tr);

    Element root = dom.getDocumentElement();
    Assert.assertEquals(WMS.VERSION_1_1_1.toString(), root.getAttribute("version"));

    return dom;
  }
  /**
   * @see
   *     org.geoserver.wms.ExtendedCapabilitiesProvider#encode(org.geoserver.wms.ExtendedCapabilitiesProvider.Translator,
   *     org.geoserver.wms.WMSInfo, org.geotools.util.Version)
   */
  public void encode(final Translator tx, final WMSInfo wms, final GetCapabilitiesRequest request)
      throws IOException {
    Version version = WMS.version(request.getVersion(), true);
    if (!WMS.VERSION_1_1_1.equals(version) || !isTiled(request)) {
      return;
    }

    String namespacePrefixFilter = request.getNamespace();
    Iterable<TileLayer> tileLayers = gwc.getTileLayersByNamespacePrefix(namespacePrefixFilter);

    for (TileLayer layer : tileLayers) {

      Set<String> layerGrids = layer.getGridSubsets();

      for (String gridId : layerGrids) {
        GridSubset grid = layer.getGridSubset(gridId);
        for (MimeType mime : layer.getMimeTypes()) {
          vendorSpecificTileset(tx, layer, grid, mime.getFormat());
        }
      }
    }
  }
  @Override
  public void encode(Translator tx, WMSInfo wms, GetCapabilitiesRequest request)
      throws IOException {
    Version requestVersion = WMS.version(request.getVersion());
    // if this is not a wms 1.3.0 request
    if (!WMS.VERSION_1_3_0.equals(requestVersion)) {
      return;
    }
    MetadataMap serviceMetadata = wms.getMetadata();
    Boolean createExtendedCapabilities =
        serviceMetadata.get(CREATE_EXTENDED_CAPABILITIES.key, Boolean.class);
    String metadataURL = (String) serviceMetadata.get(SERVICE_METADATA_URL.key);
    // Don't create extended capabilities element if mandatory content not present
    // or turned off
    if (metadataURL == null || createExtendedCapabilities != null && !createExtendedCapabilities) {
      return;
    }
    String mediaType = (String) serviceMetadata.get(SERVICE_METADATA_TYPE.key);
    String language = (String) serviceMetadata.get(LANGUAGE.key);

    // IGN : INSPIRE SCENARIO 1
    tx.start("inspire_vs:ExtendedCapabilities");
    tx.start("inspire_common:MetadataUrl");
    tx.start("inspire_common:URL");
    tx.chars(metadataURL);
    tx.end("inspire_common:URL");
    if (mediaType != null) {
      tx.start("inspire_common:MediaType");
      tx.chars(mediaType);
      tx.end("inspire_common:MediaType");
    }
    tx.end("inspire_common:MetadataUrl");
    tx.start("inspire_common:SupportedLanguages");
    language = language != null ? language : "eng";
    tx.start("inspire_common:DefaultLanguage");
    tx.start("inspire_common:Language");
    tx.chars(language);
    tx.end("inspire_common:Language");
    tx.end("inspire_common:DefaultLanguage");
    tx.end("inspire_common:SupportedLanguages");
    tx.start("inspire_common:ResponseLanguage");
    tx.start("inspire_common:Language");
    tx.chars(language);
    tx.end("inspire_common:Language");
    tx.end("inspire_common:ResponseLanguage");
    tx.end("inspire_vs:ExtendedCapabilities");
  }
    /** Encodes the service metadata section of a WMS capabilities document. */
    private void handleService() {
      start("Service");

      final WMSInfo serviceInfo = wmsConfig.getServiceInfo();
      element("Name", "OGC:WMS");
      element("Title", serviceInfo.getTitle());
      element("Abstract", serviceInfo.getAbstract());

      handleKeywordList(serviceInfo.getKeywords());

      AttributesImpl orAtts = new AttributesImpl();
      orAtts.addAttribute("", "xmlns:xlink", "xmlns:xlink", "", XLINK_NS);
      orAtts.addAttribute(XLINK_NS, "xlink:type", "xlink:type", "", "simple");

      String onlineResource = serviceInfo.getOnlineResource();
      if (onlineResource == null || onlineResource.trim().length() == 0) {
        String requestBaseUrl = request.getBaseUrl();
        onlineResource = buildURL(requestBaseUrl, null, null, URLType.SERVICE);
      } else {
        try {
          new URL(onlineResource);
        } catch (MalformedURLException e) {
          LOGGER.log(
              Level.WARNING,
              "WMS online resource seems to be an invalid URL: '" + onlineResource + "'");
        }
      }
      orAtts.addAttribute("", "xlink:href", "xlink:href", "", onlineResource);
      element("OnlineResource", null, orAtts);

      GeoServer geoServer = wmsConfig.getGeoServer();
      ContactInfo contact = geoServer.getGlobal().getContact();
      handleContactInfo(contact);

      element("Fees", serviceInfo.getFees());
      element("AccessConstraints", serviceInfo.getAccessConstraints());
      end("Service");
    }
    private void handleRequest() {
      start("Request");

      start("GetCapabilities");
      element("Format", WMS_CAPS_MIME);

      // build the service URL and make sure it ends with &
      String serviceUrl =
          buildURL(request.getBaseUrl(), "wms", params("SERVICE", "WMS"), URLType.SERVICE);
      serviceUrl = appendQueryString(serviceUrl, "");

      handleDcpType(serviceUrl, serviceUrl);
      end("GetCapabilities");

      start("GetMap");

      List<String> sortedFormats = new ArrayList<String>(getMapFormats);
      Collections.sort(sortedFormats);
      // this is a hack necessary to make cite tests pass: we need an output format
      // that is equal to the mime type as the first one....
      if (sortedFormats.contains("image/png")) {
        sortedFormats.remove("image/png");
        sortedFormats.add(0, "image/png");
      }
      for (Iterator<String> it = sortedFormats.iterator(); it.hasNext(); ) {
        element("Format", String.valueOf(it.next()));
      }

      handleDcpType(serviceUrl, null);
      end("GetMap");

      start("GetFeatureInfo");

      for (String format : wmsConfig.getAvailableFeatureInfoFormats()) {
        element("Format", format);
      }

      handleDcpType(serviceUrl, serviceUrl);
      end("GetFeatureInfo");

      start("DescribeLayer");
      element("Format", DescribeLayerResponse.DESCLAYER_MIME_TYPE);
      handleDcpType(serviceUrl, null);
      end("DescribeLayer");

      start("GetLegendGraphic");

      for (String format : getLegendGraphicFormats) {
        element("Format", format);
      }

      handleDcpType(serviceUrl, null);
      end("GetLegendGraphic");

      start("GetStyles");
      element("Format", GetStylesResponse.SLD_MIME_TYPE);
      handleDcpType(serviceUrl, null);
      end("GetStyles");

      end("Request");
    }
    /**
     * Writes layer LegendURL pointing to the user supplied icon URL, if any, or to the proper
     * GetLegendGraphic operation if an URL was not supplied by configuration file.
     *
     * <p>It is common practice to supply a URL to a WMS accesible legend graphic when it is
     * difficult to create a dynamic legend for a layer.
     *
     * @param ft The FeatureTypeInfo that holds the legendURL to write out, or<code>null</code> if
     *     dynamically generated.
     * @task TODO: figure out how to unhack legend parameters such as WIDTH, HEIGHT and FORMAT
     */
    protected void handleLegendURL(String layerName, LegendInfo legend, StyleInfo style) {
      if (legend != null) {
        if (LOGGER.isLoggable(Level.FINE)) {
          LOGGER.fine("using user supplied legend URL");
        }

        AttributesImpl attrs = new AttributesImpl();
        attrs.addAttribute("", "width", "width", "", String.valueOf(legend.getWidth()));
        attrs.addAttribute("", "height", "height", "", String.valueOf(legend.getHeight()));

        start("LegendURL", attrs);

        element("Format", legend.getFormat());
        attrs.clear();
        attrs.addAttribute("", "xmlns:xlink", "xmlns:xlink", "", XLINK_NS);
        attrs.addAttribute(XLINK_NS, "type", "xlink:type", "", "simple");
        attrs.addAttribute(XLINK_NS, "href", "xlink:href", "", legend.getOnlineResource());

        element("OnlineResource", null, attrs);

        end("LegendURL");
      } else {
        String defaultFormat = GetLegendGraphicRequest.DEFAULT_FORMAT;

        if (null == wmsConfig.getLegendGraphicOutputFormat(defaultFormat)) {
          if (LOGGER.isLoggable(Level.WARNING)) {
            LOGGER.warning(
                new StringBuffer("Default legend format (")
                    .append(defaultFormat)
                    .append(")is not supported (jai not available?), can't add LegendURL element")
                    .toString());
          }

          return;
        }

        if (LOGGER.isLoggable(Level.FINE)) {
          LOGGER.fine("Adding GetLegendGraphic call as LegendURL");
        }

        AttributesImpl attrs = new AttributesImpl();
        attrs.addAttribute(
            "", "width", "width", "", String.valueOf(GetLegendGraphicRequest.DEFAULT_WIDTH));

        // DJB: problem here is that we do not know the size of the
        // legend apriori - we need
        // to make one and find its height. Not the best way, but it
        // would work quite well.
        // This was advertising a 20*20 icon, but actually producing
        // ones of a different size.
        // An alternative is to just scale the resulting icon to what
        // the server requested, but this isnt
        // the nicest thing since warped images dont look nice. The
        // client should do the warping.

        // however, to actually estimate the size is a bit difficult.
        // I'm going to do the scaling
        // so it obeys the what the request says. For people with a
        // problem with that should consider
        // changing the default size here so that the request is for the
        // correct size.
        attrs.addAttribute(
            "", "height", "height", "", String.valueOf(GetLegendGraphicRequest.DEFAULT_HEIGHT));

        start("LegendURL", attrs);

        element("Format", defaultFormat);
        attrs.clear();

        Map<String, String> params =
            params(
                "request",
                "GetLegendGraphic",
                "format",
                defaultFormat,
                "width",
                String.valueOf(GetLegendGraphicRequest.DEFAULT_WIDTH),
                "height",
                String.valueOf(GetLegendGraphicRequest.DEFAULT_HEIGHT),
                "layer",
                layerName);
        if (style != null) {
          params.put("style", style.getName());
        }
        String legendURL = buildURL(request.getBaseUrl(), "wms", params, URLType.SERVICE);

        attrs.addAttribute("", "xmlns:xlink", "xmlns:xlink", "", XLINK_NS);
        attrs.addAttribute(XLINK_NS, "type", "xlink:type", "", "simple");
        attrs.addAttribute(XLINK_NS, "href", "xlink:href", "", legendURL);
        element("OnlineResource", null, attrs);

        end("LegendURL");
      }
    }
 private boolean isTiled(GetCapabilitiesRequest request) {
   return Boolean.valueOf(request.getRawKvp().get("TILED")).booleanValue();
 }