/**
   * Parse the UserDefinedSymbolization
   *
   * @return UserDefinedSymbolization
   * @throws XMLParsingException
   */
  protected UserDefinedSymbolization parseUserDefinedSymbolization() throws XMLParsingException {
    LOG.entering();

    String xPath = "./Capability/UserDefinedSymbolization/@SupportSLD";
    boolean supportSLD = XMLTools.getNodeAsBoolean(getRootElement(), xPath, nsContext, false);

    xPath = "./Capability/UserDefinedSymbolization/@UserLayer";
    boolean userLayer = XMLTools.getNodeAsBoolean(getRootElement(), xPath, nsContext, false);

    xPath = "./Capability/UserDefinedSymbolization/@UserStyle";
    boolean userStyle = XMLTools.getNodeAsBoolean(getRootElement(), xPath, nsContext, false);

    xPath = "./Capability/UserDefinedSymbolization/@RemoteWFS";
    boolean remoteWFS = XMLTools.getNodeAsBoolean(getRootElement(), xPath, nsContext, false);

    UserDefinedSymbolization uds =
        new UserDefinedSymbolization(
            supportSLD, userLayer,
            remoteWFS, userStyle);

    LOG.exiting();
    return uds;
  }
  /**
   * Parse Extents
   *
   * @param layerElem
   * @return Extent[]
   * @throws XMLParsingException
   */
  protected Extent[] parseExtents(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./Extent", nsContext);
    Extent[] extents = new Extent[nl.size()];
    for (int i = 0; i < extents.length; i++) {
      String name = XMLTools.getNodeAsString((Node) nl.get(i), "./@name", nsContext, null);
      String deflt = XMLTools.getNodeAsString((Node) nl.get(i), "./@default", nsContext, null);
      boolean nearestValue =
          XMLTools.getNodeAsBoolean((Node) nl.get(i), "./@nearestValue", nsContext, false);
      String value = XMLTools.getNodeAsString((Node) nl.get(i), ".", nsContext, "");
      extents[i] = new Extent(name, deflt, nearestValue, value);
    }

    LOG.exiting();
    return extents;
  }
  /**
   * parses a CS-W 2.0 transaction request
   *
   * @param id of the TransactionRequest
   * @return a new transaction parsed from the this xml-encoded request.
   * @throws XMLParsingException
   * @throws OGCWebServiceException
   */
  @Override
  public Transaction parse(String id) throws XMLParsingException, OGCWebServiceException {

    LOG.logDebug("parsing CS-W Transaction request");

    // 'service'-attribute (required, must be CSW)
    String service = XMLTools.getRequiredNodeAsString(getRootElement(), "@service", nsContext);
    if (!service.equals("CSW")) {
      ExceptionCode code = ExceptionCode.INVALIDPARAMETERVALUE;
      throw new InvalidParameterValueException("GetRecordById", "'service' must be 'CSW'", code);
    }

    String version = XMLTools.getRequiredNodeAsString(getRootElement(), "@version", nsContext);
    if (!"2.0.2".equals(version)) {
      throw new OGCWebServiceException(
          "GetRecordByIdDocument_2_0_2",
          Messages.getMessage("CSW_NOT_SUPPORTED_VERSION", "2.0.2", "2.0.2", version),
          ExceptionCode.INVALIDPARAMETERVALUE);
    }
    boolean verbose =
        XMLTools.getNodeAsBoolean(getRootElement(), "./@verboseResponse", nsContext, false);

    List<Operation> ops = new ArrayList<Operation>();

    ElementList el = XMLTools.getChildElements(getRootElement());
    for (int i = 0; i < el.getLength(); i++) {
      Element e = el.item(i);
      // TODO check for qualified name
      if ("Insert".equals(e.getLocalName())) {
        ops.add(parseInsert(e));
      } else if ("Update".equals(e.getLocalName())) {
        ops.add(parseUpdate(e));
      } else if ("Delete".equals(e.getLocalName())) {
        ops.add(parseDelete(e));
      }
    }

    // in the future the vendorSpecificParameters
    Map<String, String> vendorSpecificParameters = parseDRMParams(this.getRootElement());

    return new Transaction(version, id, vendorSpecificParameters, ops, verbose);
  }
  /**
   * 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;
  }