/**
   * Parse Contact Information
   *
   * @return ContactInfo
   * @throws XMLParsingException
   */
  protected ContactInfo parseContactInfo() throws XMLParsingException {
    LOG.entering();

    LOG.logDebug("Parsing contact information parameter.");
    Node contactInfo =
        XMLTools.getNode(getRootElement(), "./Service/ContactInformation", nsContext);
    String[] addr = XMLTools.getNodesAsStrings(contactInfo, "./ContactAddress/Address", nsContext);
    // String addrType = XMLTools.getNodeAsString( contactInfo, "./ContactAddress/AddressType",
    // nsContext, null );
    String city = XMLTools.getNodeAsString(contactInfo, "./ContactAddress/City", nsContext, null);
    String state =
        XMLTools.getNodeAsString(contactInfo, "./ContactAddress/StateOrProvince", nsContext, null);
    String pc = XMLTools.getNodeAsString(contactInfo, "./ContactAddress/PostCode", nsContext, null);
    String country =
        XMLTools.getNodeAsString(contactInfo, "./ContactAddress/Country", nsContext, null);
    String[] mail =
        XMLTools.getNodesAsStrings(contactInfo, "./ContactElectronicMailAddress", nsContext);
    Address address = new Address(state, city, country, addr, mail, pc);

    String[] phone = XMLTools.getNodesAsStrings(contactInfo, "./ContactVoiceTelephone", nsContext);
    String[] fax =
        XMLTools.getNodesAsStrings(contactInfo, "./ContactFacsimileTelephone", nsContext);

    Phone ph = new Phone(fax, phone);

    ContactInfo cont = new ContactInfo(address, null, null, null, ph);

    LOG.exiting();
    return cont;
  }
  /**
   * parses a Delete element contained in a CS-W Transaction.
   *
   * @param element
   * @return the Delete class parsed from the given Delete element.
   * @throws XMLParsingException
   * @throws MissingParameterValueException
   * @throws InvalidParameterValueException
   */
  private Delete parseDelete(Element element)
      throws XMLParsingException, MissingParameterValueException, InvalidParameterValueException {

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

    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));
    return new Delete(handle, typeName, constraint);
  }
  /**
   * returns the services indentification read from the WMPS capabilities service section
   *
   * @return ServiceIdentification
   * @throws XMLParsingException
   */
  protected ServiceIdentification parseServiceIdentification() throws XMLParsingException {
    LOG.entering();

    LOG.logDebug("Parsing service identification parameter.");
    String name = XMLTools.getNodeAsString(getRootElement(), "./Service/Name", nsContext, null);
    String title = XMLTools.getNodeAsString(getRootElement(), "./Service/Title", nsContext, name);
    String serviceAbstract =
        XMLTools.getNodeAsString(getRootElement(), "./Service/Abstract", nsContext, null);

    String[] kw =
        XMLTools.getNodesAsStrings(getRootElement(), "./Service/KeywordList/Keyword", nsContext);

    Keywords[] keywords = new Keywords[] {new Keywords(kw)};

    String fees = XMLTools.getNodeAsString(getRootElement(), "./Service/Fees", nsContext, null);

    String[] accessConstraints =
        XMLTools.getNodesAsStrings(getRootElement(), "./Service/AccessConstraints", nsContext);

    String[] acceptedVersion = new String[] {"1.0.0"};
    Code code = new Code("WMPS");
    ServiceIdentification serviceIdentification =
        new ServiceIdentification(
            code, acceptedVersion, title, serviceAbstract, keywords, fees, accessConstraints);
    LOG.exiting();
    return serviceIdentification;
  }
  /**
   * 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;
  }
  /**
   * Returns the SimpleLink from the Online Resource node in the Service element.
   *
   * @return SimpleLink
   * @throws XMLParsingException
   */
  private SimpleLink retrieveOnlineResourceSimpleLink() throws XMLParsingException {

    LOG.entering();

    String simpleLink =
        XMLTools.getNodeAsString(
            getRootElement(), "./Service/OnlineResource/@xlink:href", nsContext, null);
    SimpleLink sLink = null;
    if (simpleLink != null) {
      try {
        sLink = new SimpleLink(new URI(simpleLink));
      } catch (URISyntaxException e) {
        throw new XMLParsingException("Error parsing service online resource", e);
      }
    } else {
      try {
        /**
         * use default if no online resource is contained in the capabilities (see comment above)
         */
        sLink = new SimpleLink(new URI("http://www.opengeospatial.org/"));
      } catch (URISyntaxException neverHappens) {
        neverHappens.printStackTrace();
      }
    }
    LOG.exiting();
    return sLink;
  }
  /**
   * 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 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);
  }
  /*
   * (non-Javadoc)
   *
   * @see org.deegree.security.AbstractAuthentication#authenticate(java.util.Map,
   * javax.servlet.http.HttpServletRequest)
   */
  public User authenticate(Map<String, String> params, HttpServletRequest request)
      throws WrongCredentialsException {
    String sessionID = params.get(AUTH_PARAM_SESSIONID);
    User usr = null;
    if (sessionID != null) {
      String[] user = new String[3];
      String urlStr = initParams.get(INIT_PARAM_WAS);
      urlStr = urlStr.replaceFirst("\\[SESSIONID\\]", sessionID);
      LOG.logDebug("request WAS for user information: " + urlStr);
      Document doc = null;
      try {
        URL url = new URL(urlStr);
        XMLFragment xml = new XMLFragment(url);
        doc = xml.getRootElement().getOwnerDocument();
        user[0] = XMLTools.getNodeAsString(doc, "/User/UserName", nsContext, null);
        user[1] = XMLTools.getNodeAsString(doc, "/User/Password", nsContext, null);
      } catch (Exception e) {
        LOG.logError(e.getMessage(), e);
        throw new WrongCredentialsException(Messages.getMessage("OWSProxyServletFilter.WASACCESS"));
      }

      if (user[0] != null) {
        try {
          SecurityAccessManager sam = SecurityAccessManager.getInstance();
          usr = sam.getUserByName(user[0]);
          usr.authenticate(user[1]);
        } catch (Exception e) {
          throw new WrongCredentialsException(
              Messages.getMessage("OWSPROXY_USER_AUTH_ERROR", user[0]));
        }
      } else {
        String msg = "undefined error";
        try {
          msg = XMLTools.getNodeAsString(doc, "//ServiceException", nsContext, "general error");
        } catch (Exception e) {
          // should never happen
        }
        throw new WrongCredentialsException(msg);
      }
    }

    return usr;
  }
  /**
   * 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;
  }
  /**
   * 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;
  }
  /**
   * 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;
  }