'use strict'

module.exports = javastacktrace
javastacktrace.displayName = 'javastacktrace'
javastacktrace.aliases = []
function javastacktrace(Prism) {
  // Specification:
  // https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Throwable.html#printStackTrace()
  Prism.languages.javastacktrace = {
    // java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    // Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    // Caused by: com.example.myproject.MyProjectServletException
    // Caused by: MidLevelException: LowLevelException
    // Suppressed: Resource$CloseFailException: Resource ID = 0
    summary: {
      pattern:
        /^([\t ]*)(?:(?:Caused by:|Suppressed:|Exception in thread "[^"]*")[\t ]+)?[\w$.]+(?::.*)?$/m,
      lookbehind: true,
      inside: {
        keyword: {
          pattern:
            /^([\t ]*)(?:(?:Caused by|Suppressed)(?=:)|Exception in thread)/m,
          lookbehind: true
        },
        // the current thread if the summary starts with 'Exception in thread'
        string: {
          pattern: /^(\s*)"[^"]*"/,
          lookbehind: true
        },
        exceptions: {
          pattern: /^(:?\s*)[\w$.]+(?=:|$)/,
          lookbehind: true,
          inside: {
            'class-name': /[\w$]+$/,
            namespace: /\b[a-z]\w*\b/,
            punctuation: /\./
          }
        },
        message: {
          pattern: /(:\s*)\S.*/,
          lookbehind: true,
          alias: 'string'
        },
        punctuation: /:/
      }
    },
    // at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    // at org.hsqldb.jdbc.Util.throwError(Unknown Source) here could be some notes
    // at java.base/java.lang.Class.forName0(Native Method)
    // at Util.<init>(Unknown Source)
    // at com.foo.loader/foo@9.0/com.foo.Main.run(Main.java:101)
    // at com.foo.loader//com.foo.bar.App.run(App.java:12)
    // at acme@2.1/org.acme.Lib.test(Lib.java:80)
    // at MyClass.mash(MyClass.java:9)
    //
    // More information:
    // https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/StackTraceElement.html#toString()
    //
    // A valid Java module name is defined as:
    //   "A module name consists of one or more Java identifiers (§3.8) separated by "." tokens."
    // https://docs.oracle.com/javase/specs/jls/se9/html/jls-6.html#jls-ModuleName
    //
    // A Java module version is defined by this class:
    // https://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleDescriptor.Version.html
    // This is the implementation of the `parse` method in JDK13:
    // https://github.com/matcdac/jdk/blob/2305df71d1b7710266ae0956d73927a225132c0f/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java#L1108
    // However, to keep this simple, a version will be matched by the pattern /@[\w$.+-]*/.
    'stack-frame': {
      pattern: /^([\t ]*)at (?:[\w$./]|@[\w$.+-]*\/)+(?:<init>)?\([^()]*\)/m,
      lookbehind: true,
      inside: {
        keyword: {
          pattern: /^(\s*)at(?= )/,
          lookbehind: true
        },
        source: [
          // (Main.java:15)
          // (Main.scala:15)
          {
            pattern: /(\()\w+\.\w+:\d+(?=\))/,
            lookbehind: true,
            inside: {
              file: /^\w+\.\w+/,
              punctuation: /:/,
              'line-number': {
                pattern: /\b\d+\b/,
                alias: 'number'
              }
            }
          }, // (Unknown Source)
          // (Native Method)
          // (...something...)
          {
            pattern: /(\()[^()]*(?=\))/,
            lookbehind: true,
            inside: {
              keyword: /^(?:Native Method|Unknown Source)$/
            }
          }
        ],
        'class-name': /[\w$]+(?=\.(?:<init>|[\w$]+)\()/,
        function: /(?:<init>|[\w$]+)(?=\()/,
        'class-loader': {
          pattern: /(\s)[a-z]\w*(?:\.[a-z]\w*)*(?=\/[\w@$.]*\/)/,
          lookbehind: true,
          alias: 'namespace',
          inside: {
            punctuation: /\./
          }
        },
        module: {
          pattern: /([\s/])[a-z]\w*(?:\.[a-z]\w*)*(?:@[\w$.+-]*)?(?=\/)/,
          lookbehind: true,
          inside: {
            version: {
              pattern: /(@)[\s\S]+/,
              lookbehind: true,
              alias: 'number'
            },
            punctuation: /[@.]/
          }
        },
        namespace: {
          pattern: /(?:\b[a-z]\w*\.)+/,
          inside: {
            punctuation: /\./
          }
        },
        punctuation: /[()/.]/
      }
    },
    // ... 32 more
    // ... 32 common frames omitted
    more: {
      pattern: /^([\t ]*)\.{3} \d+ [a-z]+(?: [a-z]+)*/m,
      lookbehind: true,
      inside: {
        punctuation: /\.{3}/,
        number: /\d+/,
        keyword: /\b[a-z]+(?: [a-z]+)*\b/
      }
    }
  }
}
