<commit_before>/**
 * Jooby https://jooby.io
 * Apache License Version 2.0 https://jooby.io/LICENSE.txt
 * Copyright 2014 <NAME>
 */
package io.jooby;

import io.jooby.exception.RegistryException;
import io.jooby.exception.TypeMismatchException;
import io.jooby.internal.HashValue;
import io.jooby.internal.MissingValue;
import io.jooby.internal.SingleValue;
import io.jooby.internal.UrlParser;
import io.jooby.internal.ValueConverters;
import org.slf4j.Logger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/***
 * Like {@link Context} but with couple of default methods.
 *
 * @since 2.0.2
 * @author edgar
 */
public interface DefaultContext extends Context {

  @Nonnull @Override default <T> T require(@Nonnull Class<T> type, @Nonnull String name)
      throws RegistryException {
    return getRouter().require(type, name);
  }

  @Nonnull @Override default <T> T require(@Nonnull Class<T> type) throws RegistryException {
    return getRouter().require(type);
  }

  @Nonnull @Override default <T> T require(@Nonnull ServiceKey<T> key) throws RegistryException {
    return getRouter().require(key);
  }

  @Nullable @Override default <T> T getUser() {
    return (T) getAttributes().get("user");
  }

  @Nonnull @Override default Context setUser(@Nullable Object user) {
    getAttributes().put("user", user);
    return this;
  }

  @Override default boolean matches(String pattern) {
    return getRouter().match(pattern, getRequestPath());
  }

  /**
   * Get an attribute by his key. This is just an utility method around {@link #getAttributes()}.
   * This method look first in current context and fallback to application attributes.
   *
   * @param key Attribute key.
   * @param <T> Attribute type.
   * @return Attribute value.
   */
  @Override @Nonnull default <T> T attribute(@Nonnull String key) {
    T attribute = (T) getAttributes().get(key);
    if (attribute == null) {
      Map<String, Object> globals = getRouter().getAttributes();
      attribute = (T) globals.get(key);
    }
    return attribute;
  }

  @Override @Nonnull default Context attribute(@Nonnull String key, Object value) {
    getAttributes().put(key, value);
    return this;
  }

  @Override default @Nonnull FlashMap flash() {
    return (FlashMap) getAttributes()
        .computeIfAbsent(FlashMap.NAME, key -> FlashMap
            .create(this, getRouter().getFlashCookie().clone()));
  }

  /**
   * Get a flash attribute.
   *
   * @param name Attribute's name.
   * @return Flash attribute.
   */
  @Override default @Nonnull Value flash(@Nonnull String name) {
    return Value.create(this, name, flash().get(name));
  }

  @Override default @Nonnull Value session(@Nonnull String name) {
    Session session = sessionOrNull();
    if (session != null) {
      return session.get(name);
    }
    return Value.missing(name);
  }

  @Override default @Nonnull Session session() {
    Session session = sessionOrNull();
    if (session == null) {
      SessionStore store = getRouter().getSessionStore();
      session = store.newSession(this);
      getAttributes().put(Session.NAME, session);
    }
    return session;
  }

  @Override default @Nullable Session sessionOrNull() {
    Session session = (Session) getAttributes().get(Session.NAME);
    if (session == null) {
      Router router = getRouter();
      SessionStore store = router.getSessionStore();
      session = store.findSession(this);
      if (session != null) {
        getAttributes().put(Session.NAME, session);
      }
    }
    return session;
  }

  @Override default @Nonnull Context forward(@Nonnull String path) {
    setRequestPath(path);
    getRouter().match(this).execute(this);
    return this;
  }

  @Override default @Nonnull Value cookie(@Nonnull String name) {
    String value = cookieMap().get(name);
    return value == null ? Value.missing(name) : Value.value(this, name, value);
  }

  @Override @Nonnull default Value path(@Nonnull String name) {
    String value = pathMap().get(name);
    return value == null
        ? new MissingValue(name)
        : new SingleValue(this, name, UrlParser.decodePathSegment(value));
  }

  @Override @Nonnull default <T> T path(@Nonnull Class<T> type) {
    return path().to(type);
  }

  @Override @Nonnull default ValueNode path() {
    HashValue path = new HashValue(this, null);
    for (Map.Entry<String, String> entry : pathMap().entrySet()) {
      path.put(entry.getKey(), entry.getValue());
    }
    return path;
  }

  @Override @Nonnull default ValueNode query(@Nonnull String name) {
    return query().get(name);
  }

  @Override @Nonnull default String queryString() {
    return query().queryString();
  }

  @Override @Nonnull default <T> T query(@Nonnull Class<T> type) {
    return query().to(type);
  }

  @Override @Nonnull default Map<String, String> queryMap() {
    return query().toMap();
  }

  @Override @Nonnull default Map<String, List<String>> queryMultimap() {
    return query().toMultimap();
  }

  @Override @Nonnull default Value header(@Nonnull String name) {
    return header().get(name);
  }

  @Override @Nonnull default Map<String, String> headerMap() {
    return header().toMap();
  }

  @Override @Nonnull default Map<String, List<String>> headerMultimap() {
    return header().toMultimap();
  }

  @Override default boolean accept(@Nonnull MediaType contentType) {
    Value accept = header(ACCEPT);
    return accept.isMissing() || contentType.matches(accept.value());
  }

  @Override default MediaType accept(@Nonnull List<MediaType> produceTypes) {
    List<MediaType> acceptTypes = MediaType.parse(header(ACCEPT).valueOrNull());
    MediaType result = null;
    for (MediaType acceptType : acceptTypes) {
      for (MediaType produceType : produceTypes) {
        if (produceType.matches(acceptType)) {
          if (result == null) {
            result = produceType;
          } else {
            MediaType max = MediaType.MOST_SPECIFIC.apply(result, produceType);
            if (max != result) {
              result = max;
            }
          }
        }
      }
    }
    return result;
  }

  @Override default @Nonnull String getRequestURL() {
    return getRequestURL("");
  }

  @Override default @Nonnull String getRequestURL(@Nonnull String path) {
    String scheme = getScheme();
    String host = getHost();
    int port = getPort();
    StringBuilder url = new StringBuilder();
    url.append(scheme).append("://").append(host);
    if (port > 0 && port != PORT && port != SECURE_PORT) {
      url.append(":").append(port);
    }
    if (path == null || path.length() == 0) {
      url.append(getRequestPath());
    } else {
      String contextPath = getContextPath();
      if (!contextPath.equals("/") && !path.startsWith(contextPath)) {
        url.append(contextPath);
      }
      url.append(path);
    }
    url.append(queryString());

    return url.toString();
  }

  @Override @Nullable default MediaType getRequestType() {
    Value contentType = header("Content-Type");
    return contentType.isMissing() ? null : MediaType.valueOf(contentType.value());
  }

  @Override @Nonnull default MediaType getRequestType(MediaType defaults) {
    Value contentType = header("Content-Type");
    return contentType.isMissing() ? defaults : MediaType.valueOf(contentType.value());
  }

  @Override default long getRequestLength() {
    Value contentLength = header("Content-Length");
    return contentLength.isMissing() ? -1 : contentLength.longValue();
  }

  @Override default @Nullable String getHostAndPort() {
    Optional<String> header = getRouter().isTrustProxy()
        ? header("X-Forwarded-Host").toOptional()
        : Optional.empty();
    String value = header
        .orElseGet(() -> header("Host").value(getServerHost() + ":" + getServerPort()));
    int i = value.indexOf(',');
    String host = i > 0 ? value.substring(0, i).trim() : value;
    if (host.startsWith("[") && host.endsWith("]")) {
      return host.substring(1, host.length() - 1).trim();
    }
    return host;
  }

  @Override default @Nonnull String getServerHost() {
    String host = getRouter().getServerOptions().getHost();
    return host.equals("0.0.0.0") ? "localhost" : host;
  }

  @Override default int getServerPort() {
    ServerOptions options = getRouter().getServerOptions();
    return isSecure() ? options.getSecurePort() : options.getPort();
  }

  @Override default int getPort() {
    String hostAndPort = getHostAndPort();
    if (hostAndPort != null) {<commit_msg>Context getHostAndPort without securePort set -> NPE fix #2074<commit_after>/**
 * Jooby https://jooby.io
 * Apache License Version 2.0 https://jooby.io/LICENSE.txt
 * Copyright 2014 <NAME>
 */
package io.jooby;

import static java.util.Optional.ofNullable;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.slf4j.Logger;

import io.jooby.exception.RegistryException;
import io.jooby.exception.TypeMismatchException;
import io.jooby.internal.HashValue;
import io.jooby.internal.MissingValue;
import io.jooby.internal.SingleValue;
import io.jooby.internal.UrlParser;
import io.jooby.internal.ValueConverters;

/***
 * Like {@link Context} but with couple of default methods.
 *
 * @since 2.0.2
 * @author edgar
 */
public interface DefaultContext extends Context {

  @Nonnull @Override default <T> T require(@Nonnull Class<T> type, @Nonnull String name)
      throws RegistryException {
    return getRouter().require(type, name);
  }

  @Nonnull @Override default <T> T require(@Nonnull Class<T> type) throws RegistryException {
    return getRouter().require(type);
  }

  @Nonnull @Override default <T> T require(@Nonnull ServiceKey<T> key) throws RegistryException {
    return getRouter().require(key);
  }

  @Nullable @Override default <T> T getUser() {
    return (T) getAttributes().get("user");
  }

  @Nonnull @Override default Context setUser(@Nullable Object user) {
    getAttributes().put("user", user);
    return this;
  }

  @Override default boolean matches(String pattern) {
    return getRouter().match(pattern, getRequestPath());
  }

  /**
   * Get an attribute by his key. This is just an utility method around {@link #getAttributes()}.
   * This method look first in current context and fallback to application attributes.
   *
   * @param key Attribute key.
   * @param <T> Attribute type.
   * @return Attribute value.
   */
  @Override @Nonnull default <T> T attribute(@Nonnull String key) {
    T attribute = (T) getAttributes().get(key);
    if (attribute == null) {
      Map<String, Object> globals = getRouter().getAttributes();
      attribute = (T) globals.get(key);
    }
    return attribute;
  }

  @Override @Nonnull default Context attribute(@Nonnull String key, Object value) {
    getAttributes().put(key, value);
    return this;
  }

  @Override default @Nonnull FlashMap flash() {
    return (FlashMap) getAttributes()
        .computeIfAbsent(FlashMap.NAME, key -> FlashMap
            .create(this, getRouter().getFlashCookie().clone()));
  }

  /**
   * Get a flash attribute.
   *
   * @param name Attribute's name.
   * @return Flash attribute.
   */
  @Override default @Nonnull Value flash(@Nonnull String name) {
    return Value.create(this, name, flash().get(name));
  }

  @Override default @Nonnull Value session(@Nonnull String name) {
    Session session = sessionOrNull();
    if (session != null) {
      return session.get(name);
    }
    return Value.missing(name);
  }

  @Override default @Nonnull Session session() {
    Session session = sessionOrNull();
    if (session == null) {
      SessionStore store = getRouter().getSessionStore();
      session = store.newSession(this);
      getAttributes().put(Session.NAME, session);
    }
    return session;
  }

  @Override default @Nullable Session sessionOrNull() {
    Session session = (Session) getAttributes().get(Session.NAME);
    if (session == null) {
      Router router = getRouter();
      SessionStore store = router.getSessionStore();
      session = store.findSession(this);
      if (session != null) {
        getAttributes().put(Session.NAME, session);
      }
    }
    return session;
  }

  @Override default @Nonnull Context forward(@Nonnull String path) {
    setRequestPath(path);
    getRouter().match(this).execute(this);
    return this;
  }

  @Override default @Nonnull Value cookie(@Nonnull String name) {
    String value = cookieMap().get(name);
    return value == null ? Value.missing(name) : Value.value(this, name, value);
  }

  @Override @Nonnull default Value path(@Nonnull String name) {
    String value = pathMap().get(name);
    return value == null
        ? new MissingValue(name)
        : new SingleValue(this, name, UrlParser.decodePathSegment(value));
  }

  @Override @Nonnull default <T> T path(@Nonnull Class<T> type) {
    return path().to(type);
  }

  @Override @Nonnull default ValueNode path() {
    HashValue path = new HashValue(this, null);
    for (Map.Entry<String, String> entry : pathMap().entrySet()) {
      path.put(entry.getKey(), entry.getValue());
    }
    return path;
  }

  @Override @Nonnull default ValueNode query(@Nonnull String name) {
    return query().get(name);
  }

  @Override @Nonnull default String queryString() {
    return query().queryString();
  }

  @Override @Nonnull default <T> T query(@Nonnull Class<T> type) {
    return query().to(type);
  }

  @Override @Nonnull default Map<String, String> queryMap() {
    return query().toMap();
  }

  @Override @Nonnull default Map<String, List<String>> queryMultimap() {
    return query().toMultimap();
  }

  @Override @Nonnull default Value header(@Nonnull String name) {
    return header().get(name);
  }

  @Override @Nonnull default Map<String, String> headerMap() {
    return header().toMap();
  }

  @Override @Nonnull default Map<String, List<String>> headerMultimap() {
    return header().toMultimap();
  }

  @Override default boolean accept(@Nonnull MediaType contentType) {
    Value accept = header(ACCEPT);
    return accept.isMissing() || contentType.matches(accept.value());
  }

  @Override default MediaType accept(@Nonnull List<MediaType> produceTypes) {
    List<MediaType> acceptTypes = MediaType.parse(header(ACCEPT).valueOrNull());
    MediaType result = null;
    for (MediaType acceptType : acceptTypes) {
      for (MediaType produceType : produceTypes) {
        if (produceType.matches(acceptType)) {
          if (result == null) {
            result = produceType;
          } else {
            MediaType max = MediaType.MOST_SPECIFIC.apply(result, produceType);
            if (max != result) {
              result = max;
            }
          }
        }
      }
    }
    return result;
  }

  @Override default @Nonnull String getRequestURL() {
    return getRequestURL("");
  }

  @Override default @Nonnull String getRequestURL(@Nonnull String path) {
    String scheme = getScheme();
    String host = getHost();
    int port = getPort();
    StringBuilder url = new StringBuilder();
    url.append(scheme).append("://").append(host);
    if (port > 0 && port != PORT && port != SECURE_PORT) {
      url.append(":").append(port);
    }
    if (path == null || path.length() == 0) {
      url.append(getRequestPath());
    } else {
      String contextPath = getContextPath();
      if (!contextPath.equals("/") && !path.startsWith(contextPath)) {
        url.append(contextPath);
      }
      url.append(path);
    }
    url.append(queryString());

    return url.toString();
  }

  @Override @Nullable default MediaType getRequestType() {
    Value contentType = header("Content-Type");
    return contentType.isMissing() ? null : MediaType.valueOf(contentType.value());
  }

  @Override @Nonnull default MediaType getRequestType(MediaType defaults) {
    Value contentType = header("Content-Type");
    return contentType.isMissing() ? defaults : MediaType.valueOf(contentType.value());
  }

  @Override default long getRequestLength() {
    Value contentLength = header("Content-Length");
    return contentLength.isMissing() ? -1 : contentLength.longValue();
  }

  @Override default @Nullable String getHostAndPort() {
    Optional<String> header = getRouter().isTrustProxy()
        ? header("X-Forwarded-Host").toOptional()
        : Optional.empty();
    String value = header
        .orElseGet(() ->
            ofNullable(header("Host").valueOrNull())
                .orElseGet(() -> getServerHost() + ":" + getServerPort())
        );
    int i = value.indexOf(',');
    String host = i > 0 ? value.substring(0, i).trim() : value;
    if (host.startsWith("[") && host.endsWith("]")) {
      return host.substring(1, host.length() - 1).trim();
    }
    return host;
  }

  @Override default @Nonnull String getServerHost() {
    String host = getRouter().getServerOptions().getHost();
    return host.equals("0.0.0.0") ? "localhost" : host;
  }

  @Override default int getServerPort() {
    ServerOptions options = getRouter().getServerOptions();
    return isSecure()
        // Buggy proxy where it report a https scheme but there is no HTTPS configured option
        ? ofNullable(options.getSecurePort()).orElse(options.getPort())
        : options.getPort();
  }

  @Override default int getPort() {
    String hostAndPort = getHostAndPort();
    if (hostAndPort != null) {
{'fraction_non_alphanumeric': 0.13382248261512347, 'fraction_numerical': 0.0025187537644417676, 'mean_word_length': 3.457896021479131, 'pattern_counts': {'":': 6, '<': 53, '<?xml version=': 0, '>': 63, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, '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': '127645', 'n_tokens_mistral': 5393, 'n_tokens_neox': 5273, 'n_words': 1381}