/**
   * parses a Update element contained in a CS-W Transaction.
   *
   * @param element
   * @return the update class containing all parsed values
   * @throws XMLParsingException
   * @throws MissingParameterValueException
   * @throws InvalidParameterValueException
   */
  private Update parseUpdate(Element element)
      throws XMLParsingException, MissingParameterValueException, InvalidParameterValueException {

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

    String handle = XMLTools.getNodeAsString(element, "@handle", nsContext, null);
    String tmp = XMLTools.getNodeAsString(element, "@typeName", nsContext, null);
    URI typeName = null;
    if (tmp != null) {
      // part of the corrected CS-W 2.0 spec
      try {
        typeName = new URI(tmp);
      } catch (Exception e) {
        throw new XMLParsingException("if defined attribute 'typeName' must be a valid URI");
      }
    }

    Element elem = (Element) XMLTools.getRequiredNode(element, "./csw202:Constraint", nsContext);
    String ver = XMLTools.getNodeAsString(elem, "@version", nsContext, null);
    if (ver == null) {
      String s = Messages.getMessage("CSW_MISSING_CONSTRAINT_VERSION");
      throw new MissingParameterValueException(s);
    }
    if (!"1.0.0".equals(ver) && !"1.1.0".equals(ver)) {
      String s = Messages.getMessage("CSW_INVALID_CONSTRAINT_VERSION", ver);
      throw new InvalidParameterValueException(s);
    }

    elem = (Element) XMLTools.getRequiredNode(elem, "./ogc:Filter", nsContext);

    Filter constraint = AbstractFilter.buildFromDOM(elem, "1.0.0".equals(ver));

    List<Node> children = null;
    List<Node> rp = XMLTools.getNodes(getRootElement(), "./csw202:RecordProperty", nsContext);
    if (rp.size() != 0) {
      // at the moment will always be null because it is part of the
      // CS-W 2.0 corrected version that will not be implemented yet
    } else {
      children = XMLTools.getNodes(element, "./child::*", nsContext);
      if (children.size() == 0) {
        throw new XMLParsingException("one record must be defined within a CS-W update element");
      }
    }
    return new Update(handle, typeName, constraint, (Element) children.get(0), null);
  }
  /**
   * Creates a <code>DCPType</code> object from the passed <code>DCP</code> element.
   * <p>
   * NOTE: Currently the <code>OnlineResources</code> included in the <code>DCPType</code> are
   * just stored as simple <code>URLs</code> (not as <code>OnLineResource</code> instances)!
   * <p>
   * NOTE: In an <code>OGCStandardCapabilitiesDocument</code> the <code>XLinks</code> (the
   * <code>URLs</code>) are stored in separate elements (<code>OnlineResource</code>), in
   * an <code>OGCCommonCapabilitiesDocument</code> they are the
   * <code>Get<code>/<code>Post</code> elements themselves.
   *
   * @param element
   *
   * @return created <code>DCPType</code>
   * @throws XMLParsingException
   *
   * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities
   */
  @Override
  protected DCPType getDCP(Element element) throws XMLParsingException {
    LOG.entering();
    DCPType dcpType = null;
    try {
      Element elem = (Element) XMLTools.getRequiredNode(element, "HTTP", nsContext);
      List nl = XMLTools.getNodes(elem, "Get", nsContext);

      URL[] get = new URL[nl.size()];
      for (int i = 0; i < get.length; i++) {
        String s = XMLTools.getNodeAsString((Node) nl.get(i), "./@xlink:href", nsContext, null);
        if (s == null) {
          s =
              XMLTools.getRequiredNodeAsString(
                  (Node) nl.get(i), "./OnlineResource/@xlink:href", nsContext);
        }
        get[i] = new URL(s);
      }
      nl = XMLTools.getNodes(elem, "Post", nsContext);

      URL[] post = new URL[nl.size()];
      for (int i = 0; i < post.length; i++) {
        String s = XMLTools.getNodeAsString((Node) nl.get(i), "./@xlink:href", nsContext, null);
        if (s == null) {
          s =
              XMLTools.getRequiredNodeAsString(
                  (Node) nl.get(i), "./OnlineResource/@xlink:href", nsContext);
        }
        post[i] = new URL(s);
      }
      Protocol protocol = new HTTP(get, post);
      dcpType = new DCPType(protocol);
    } catch (MalformedURLException e) {
      throw new XMLParsingException(
          "Couldn't parse DCPType onlineresource URL about: " + StringTools.stackTraceToString(e));
    }
    LOG.exiting();
    return dcpType;
  }
  /**
   * Parse Identifiers
   *
   * @param layerElem
   * @return Identifier[]
   * @throws XMLParsingException
   */
  protected Identifier[] parseIdentifiers(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./Identifier", nsContext);
    Identifier[] identifiers = new Identifier[nl.size()];
    for (int i = 0; i < identifiers.length; i++) {
      String value = XMLTools.getStringValue((Node) nl.get(i));
      String authority = XMLTools.getNodeAsString(layerElem, "./@authority", nsContext, null);
      identifiers[i] = new Identifier(value, authority);
    }

    LOG.exiting();
    return identifiers;
  }
  /**
   * Parse AuthorityURL
   *
   * @param layerElem
   * @return AuthorityURL[]
   * @throws XMLParsingException
   */
  protected AuthorityURL[] parseAuthorityURLs(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./AuthorityURL", nsContext);
    AuthorityURL[] authorityURLs = new AuthorityURL[nl.size()];
    for (int i = 0; i < authorityURLs.length; i++) {
      String name = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./@name", nsContext);
      Element tmp =
          (Element) XMLTools.getRequiredNode((Node) nl.get(i), "./OnlineResource", nsContext);
      OnlineResource olr = parseOnLineResource(tmp);
      authorityURLs[i] = new AuthorityURL(name, olr.getLinkage().getHref());
    }

    LOG.exiting();
    return authorityURLs;
  }
  /**
   * Parse Dimensions
   *
   * @param layerElem
   * @return Dimension[]
   * @throws XMLParsingException
   */
  protected Dimension[] parseDimensions(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./Dimension", nsContext);
    Dimension[] dimensions = new Dimension[nl.size()];
    for (int i = 0; i < dimensions.length; i++) {
      String name = XMLTools.getNodeAsString((Node) nl.get(i), "./@name", nsContext, null);
      String units = XMLTools.getNodeAsString((Node) nl.get(i), "./@units", nsContext, null);
      String unitSymbol =
          XMLTools.getNodeAsString((Node) nl.get(i), "./@unitSymbol", nsContext, null);
      dimensions[i] = new Dimension(name, units, unitSymbol);
    }

    LOG.exiting();
    return dimensions;
  }
  /**
   * Parse FeatureListURL
   *
   * @param layerElem
   * @return FeatureListURL[]
   * @throws XMLParsingException
   */
  protected FeatureListURL[] parseFeatureListURL(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./FeatureListURL", nsContext);
    FeatureListURL[] flURL = new FeatureListURL[nl.size()];
    for (int i = 0; i < flURL.length; i++) {

      String format = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./Format", nsContext);
      Element tmp =
          (Element) XMLTools.getRequiredNode((Node) nl.get(i), "./OnlineResource", nsContext);
      OnlineResource olr = parseOnLineResource(tmp);
      flURL[i] = new FeatureListURL(format, olr.getLinkage().getHref());
    }

    LOG.exiting();
    return flURL;
  }
  /**
   * Parse MetadataURL
   *
   * @param layerElem
   * @return MetadataURL[]
   * @throws XMLParsingException
   */
  protected MetadataURL[] parseMetadataURLs(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./MetadataURL", nsContext);
    MetadataURL[] metadataURL = new MetadataURL[nl.size()];
    for (int i = 0; i < metadataURL.length; i++) {
      String type = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./@type", nsContext);
      String format = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./Format", nsContext);
      Element tmp =
          (Element) XMLTools.getRequiredNode((Node) nl.get(i), "./OnlineResource", nsContext);
      OnlineResource olr = parseOnLineResource(tmp);
      metadataURL[i] = new MetadataURL(type, format, olr.getLinkage().getHref());
    }

    LOG.exiting();
    return metadataURL;
  }
  /**
   * 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;
  }
  /**
   * Parse Legend URL
   *
   * @param node
   * @return LegendURL[]
   * @throws XMLParsingException
   */
  protected LegendURL[] parseLegendURL(Node node) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(node, "./LegendURL", nsContext);
    LegendURL[] lURL = new LegendURL[nl.size()];
    for (int i = 0; i < lURL.length; i++) {
      int width = XMLTools.getRequiredNodeAsInt((Node) nl.get(i), "./@width", nsContext);
      int height = XMLTools.getRequiredNodeAsInt((Node) nl.get(i), "./@height", nsContext);
      String format = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./Format", nsContext);
      Element tmp =
          (Element) XMLTools.getRequiredNode((Node) nl.get(i), "./OnlineResource", nsContext);
      OnlineResource olr = parseOnLineResource(tmp);
      lURL[i] = new LegendURL(width, height, format, olr.getLinkage().getHref());
    }

    LOG.exiting();
    return lURL;
  }
  /**
   * Parse Styles
   *
   * @param layerElem
   * @return Style[]
   * @throws XMLParsingException
   */
  protected Style[] parseStyles(Element layerElem) throws XMLParsingException {
    LOG.entering();

    List nl = XMLTools.getNodes(layerElem, "./Style", nsContext);
    Style[] styles = new Style[nl.size()];
    for (int i = 0; i < styles.length; i++) {
      String name = XMLTools.getRequiredNodeAsString((Node) nl.get(i), "./Name", nsContext);
      String title = XMLTools.getNodeAsString((Node) nl.get(i), "./Title", nsContext, null);
      String styleAbstract =
          XMLTools.getNodeAsString((Node) nl.get(i), "./Abstract", nsContext, null);
      LegendURL[] legendURLs = parseLegendURL((Node) nl.get(i));
      StyleURL styleURL = parseStyleURL((Node) nl.get(i));
      StyleSheetURL styleSheetURL = parseStyleSheetURL((Node) nl.get(i));

      styles[i] = new Style(name, title, styleAbstract, legendURLs, styleSheetURL, styleURL, null);
    }

    LOG.exiting();
    return styles;
  }
  /**
   * parses a Insert element contained in a CS-W Transaction.
   *
   * @param element
   * @return an Insert instance
   * @throws XMLParsingException
   */
  private Insert parseInsert(Element element) throws XMLParsingException {

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

    String handle = XMLTools.getNodeAsString(element, "@handle", nsContext, "");
    List<Element> recList = new ArrayList<Element>();
    List<Node> children = XMLTools.getNodes(element, "*", nsContext);
    if (children.size() == 0) {
      LOG.logError("at least one record must be defined within a CS-W insert element");
      throw new XMLParsingException(
          "at least one record must be defined within a CS-W insert element");
    }

    for (Object n : children) {
      LOG.logDebug(
          "TransactionDocument(insert): adding the element: "
              + element.getLocalName()
              + " to the records list. ");
      recList.add((Element) n);
    }

    // if no ebrim is done, create the old insert class.
    return new Insert(handle, recList);
  }
  /**
   * 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;
  }