<commit_before>/**
 * Copyright (C) 2010 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.xforms.event.events

import org.apache.log4j.Level
import org.orbeon.oxf.common.ValidationException
import org.orbeon.oxf.resources.URLFactory
import org.orbeon.oxf.util.ScalaUtils._
import org.orbeon.oxf.util._
import org.orbeon.oxf.xforms.XFormsProperties
import org.orbeon.oxf.xforms.event.XFormsEvent
import org.orbeon.oxf.xforms.event.XFormsEvent._
import org.orbeon.oxf.xforms.submission.XFormsModelSubmission
import org.orbeon.oxf.xml._
import org.orbeon.saxon.om._

import scala.collection.JavaConverters._
import scala.collection.immutable
import scala.util.Try
import scala.util.control.NonFatal

// Helper trait for xforms-submit-done/xforms-submit-error
trait SubmitResponseEvent extends XFormsEvent {

  def connectionResult: Option[ConnectionResult]
  final def headers = connectionResult map (_.headers)

  override implicit def indentedLogger = containingDocument.getIndentedLogger(XFormsModelSubmission.LOGGING_CATEGORY)
  override def lazyProperties = getters(this, SubmitResponseEvent.Getters)
  override def newPropertyName(name: String) = SubmitResponseEvent.Deprecated.get(name) orElse super.newPropertyName(name)
}

private object SubmitResponseEvent {

  import org.orbeon.oxf.util.XPathCache._
  import org.orbeon.oxf.xml.NamespaceMapping.EMPTY_MAPPING

  // "Zero or more elements, each one representing a content header in the error response received by a
  // failed submission. The returned node-set is empty if the failed submission did not receive an error
  // response or if there were no headers. Each element has a local name of header with no namespace URI and
  // two child elements, name and value, whose string contents are the name and value of the header,
  // respectively."
  def headersDocument(headersOpt: Option[Iterable[(String, immutable.Seq[String])]]): Option[DocumentInfo] =
    headersOpt filter (_.nonEmpty) map { headers ⇒
      val sb = new StringBuilder
      sb.append("<headers>")
      for ((name, values) ← headers) {
        sb.append("<header><name>")
        sb.append(XMLUtils.escapeXMLMinimal(name))
        sb.append("</name>")
        for (value ← values) {
          sb.append("<value>")
          sb.append(XMLUtils.escapeXMLMinimal(value))
          sb.append("</value>")
        }
        sb.append("</header>")
      }
      sb.append("</headers>")
      TransformerUtils.stringToTinyTree(XPath.GlobalConfiguration, sb.toString, false, false) // handleXInclude, handleLexical
    }

  def headerElements(e: SubmitResponseEvent): Option[Seq[Item]] = headersDocument(e.headers) map { document: Item ⇒
    evaluateKeepItems(
      Seq(document).asJava,
      1,
      "/headers/header",
      EMPTY_MAPPING,
      null, null, null, null,
      e.locationData,
      e.containingDocument.getRequestStats.addXPathStat).asScala
  }

  // For a given event, temporarily keep a reference to the body so that it's possible to call
  // `event('response-body')` multiple times.
  private var _body: Option[Option[String Either DocumentInfo]] = None

  def body(e: SubmitResponseEvent): Option[AnyRef] = {
    implicit val logger = e.indentedLogger

    def readOrReturn(cxr: ConnectionResult) =
      _body getOrElse {
        val result = tryToReadBody(cxr)
        _body = Some(result)
        result
      }

    e.connectionResult flatMap readOrReturn map {<commit_msg>For #2782: cache in the right place<commit_after>/**
 * Copyright (C) 2010 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.xforms.event.events

import org.apache.log4j.Level
import org.orbeon.oxf.common.ValidationException
import org.orbeon.oxf.resources.URLFactory
import org.orbeon.oxf.util.ScalaUtils._
import org.orbeon.oxf.util._
import org.orbeon.oxf.xforms.XFormsProperties
import org.orbeon.oxf.xforms.event.XFormsEvent
import org.orbeon.oxf.xforms.event.XFormsEvent._
import org.orbeon.oxf.xforms.submission.XFormsModelSubmission
import org.orbeon.oxf.xml._
import org.orbeon.saxon.om._

import scala.collection.JavaConverters._
import scala.collection.immutable
import scala.util.Try
import scala.util.control.NonFatal

// Helper trait for xforms-submit-done/xforms-submit-error
trait SubmitResponseEvent extends XFormsEvent {

  def connectionResult: Option[ConnectionResult]
  final def headers = connectionResult map (_.headers)

  // For a given event, temporarily keep a reference to the body so that it's possible to call
  // `event('response-body')` multiple times.
  var cachedBody: Option[Option[String Either DocumentInfo]] = None

  override implicit def indentedLogger = containingDocument.getIndentedLogger(XFormsModelSubmission.LOGGING_CATEGORY)
  override def lazyProperties = getters(this, SubmitResponseEvent.Getters)
  override def newPropertyName(name: String) = SubmitResponseEvent.Deprecated.get(name) orElse super.newPropertyName(name)
}

private object SubmitResponseEvent {

  import org.orbeon.oxf.util.XPathCache._
  import org.orbeon.oxf.xml.NamespaceMapping.EMPTY_MAPPING

  // "Zero or more elements, each one representing a content header in the error response received by a
  // failed submission. The returned node-set is empty if the failed submission did not receive an error
  // response or if there were no headers. Each element has a local name of header with no namespace URI and
  // two child elements, name and value, whose string contents are the name and value of the header,
  // respectively."
  def headersDocument(headersOpt: Option[Iterable[(String, immutable.Seq[String])]]): Option[DocumentInfo] =
    headersOpt filter (_.nonEmpty) map { headers ⇒
      val sb = new StringBuilder
      sb.append("<headers>")
      for ((name, values) ← headers) {
        sb.append("<header><name>")
        sb.append(XMLUtils.escapeXMLMinimal(name))
        sb.append("</name>")
        for (value ← values) {
          sb.append("<value>")
          sb.append(XMLUtils.escapeXMLMinimal(value))
          sb.append("</value>")
        }
        sb.append("</header>")
      }
      sb.append("</headers>")
      TransformerUtils.stringToTinyTree(XPath.GlobalConfiguration, sb.toString, false, false) // handleXInclude, handleLexical
    }

  def headerElements(e: SubmitResponseEvent): Option[Seq[Item]] = headersDocument(e.headers) map { document: Item ⇒
    evaluateKeepItems(
      Seq(document).asJava,
      1,
      "/headers/header",
      EMPTY_MAPPING,
      null, null, null, null,
      e.locationData,
      e.containingDocument.getRequestStats.addXPathStat).asScala
  }

  def body(e: SubmitResponseEvent): Option[AnyRef] = {
    implicit val logger = e.indentedLogger

    def readOrReturn(cxr: ConnectionResult) =
      e.cachedBody getOrElse {
        val result = tryToReadBody(cxr)
        e.cachedBody = Some(result)
        result
      }

    e.connectionResult flatMap readOrReturn map {
{'fraction_non_alphanumeric': 0.09263157894736843, 'fraction_numerical': 0.002476780185758514, 'mean_word_length': 3.8301435406698565, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6002338', 'n_tokens_mistral': 2371, 'n_tokens_neox': 2340, 'n_words': 705}