/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package javaguide.binder.models;

import play.mvc.PathBindable;

// #declaration
public class User implements PathBindable<User> {

  public Long id;
  public String name;
  // #declaration

  // #bind
  @Override
  public User bind(String key, String id) {

    // findById meant to be lightweight operation
    User user = findById(Long.valueOf(id));
    if (user == null) {
      throw new IllegalArgumentException("User with id " + id + " not found");
    }
    return user;
  }

  @Override
  public String unbind(String key) {
    return String.valueOf(id);
  }
  // #bind

  @Override
  public String javascriptUnbind() {
    return "function(k,v) {\n" + "    return v.id;" + "}";
  }

  // stubbed test
  // designed to be lightweight operation
  private User findById(Long id) {
    if (id > 3) return null;
    User user = new User();
    user.id = id;
    user.name = "User " + String.valueOf(id);
    return user;
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.i18n;

import play.libs.Scala;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/** Manages languages in Play */
@Singleton
public class Langs {
  private final play.api.i18n.Langs langs;
  private final List<Lang> availables;

  @Inject
  public Langs(play.api.i18n.Langs langs) {
    this.langs = langs;
    List<Lang> availables = new ArrayList<>();
    for (play.api.i18n.Lang lang : Scala.asJava(langs.availables())) {
      availables.add(new Lang(lang));
    }
    this.availables = Collections.unmodifiableList(availables);
  }

  /**
   * The available languages.
   *
   *
These can be configured in <tt>application.conf</tt>, like so:
   *
   *

   * play.i18n.langs = ["fr", "en", "de"]
   *

   *
   * @return The available languages.
   */
  public List<Lang> availables() {
    return availables;
  }

  /**
   * Select a preferred language, given the list of candidates.
   *
   *
Will select the preferred language, based on what languages are available, or return the
   * default language if none of the candidates are available.
   *
   * @param candidates The candidate languages
   * @return The preferred language
   */
  public Lang preferred(Collection<Lang> candidates) {
    return new Lang(
        langs.preferred((scala.collection.immutable.Seq) Scala.asScala(candidates).toSeq()));
  }

  /** @return the Scala version for this Langs. */
  public play.api.i18n.Langs asScala() {
    return langs;
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.mvc;

import play.core.Paths;

/**
 * Defines a 'call', describing an HTTP request. For example used to create links or populate
 * redirect data.
 *
 * <p>These values are usually generated by the reverse router.
 */
public abstract class Call {

  private static java.util.Random rand = new java.util.Random();

  /**
   * The request URL.
   *
   * @return the url
   */
  public abstract String url();

  /**
   * The request HTTP method.
   *
   * @return the http method (e.g. "GET")
   */
  public abstract String method();

  /**
   * The fragment of the URL.
   *
   * @return the fragment (without leading '#' character)
   */
  public abstract String fragment();

  /**
   * Append a unique identifier to the URL.
   *
   * @return a copy if this call with a unique identifier to this url
   */
  public Call unique() {
    return new play.api.mvc.Call(method(), this.uniquify(this.url()), fragment());
  }

  protected final String uniquify(String url) {
    return url + ((url.indexOf('?') == -1) ? "?" : "&") + rand.nextLong();
  }

  /**
   * Returns a new Call with the given fragment.
   *
   * @param fragment the URL fragment
   * @return a copy of this call that contains the fragment
   */
  public Call withFragment(String fragment) {
    return new play.api.mvc.Call(method(), url(), fragment);
  }

  /**
   * Returns the fragment (including the leading "#") if this call has one.
   *
   * @return the fragment, with leading "#"
   */
  protected String appendFragment() {
    if (this.fragment() != null && !this.fragment().trim().isEmpty()) {
      return "#" + this.fragment();
    } else {
      return "";
    }
  }

  /**
   * Transform this call to an absolute URL.
   *
   * @param request used to identify the host and protocol that should base this absolute URL
   * @return the absolute URL string
   */
  public String absoluteURL(Http.Request request) {
    return absoluteURL(request.secure(), request.host());
  }

  /**
   * Transform this call to an absolute URL.
   *
   * @param request used to identify the host that should base this absolute URL
   * @param secure true if the absolute URL should use HTTPS protocol
   * @return the absolute URL string
   */
  public String absoluteURL(Http.Request request, boolean secure) {
    return absoluteURL(secure, request.host());
  }

  /**
   * Transform this call to an absolute URL.
   *
   * @param secure true if the absolute URL should use HTTPS protocol instead of HTTP
   * @param host the absolute URL's domain
   * @return the absolute URL string
   */
  public String absoluteURL(boolean secure, String host) {
    return "http" + (secure ? "s" : "") + "://" + host + this.url() + this.appendFragment();
  }

  /**
   * Transform this call to an WebSocket URL.
   *
   * @param request used as the base for forming the WS url
   * @return the websocket url string
   */
  public String webSocketURL(Http.Request request) {
    return webSocketURL(request.secure(), request.host());
  }

  /**
   * Transform this call to an WebSocket URL.
   *
   * @param request used to identify the host for the absolute URL
   * @param secure true if it should be a wss rather than ws URL
   * @return the websocket URL string
   */
  public String webSocketURL(Http.Request request, boolean secure) {
    return webSocketURL(secure, request.host());
  }

  /**
   * Transform this call to an WebSocket URL.
   *
   * @param host the host for the absolute URL.
   * @param secure true if it should be a wss rather than ws URL
   * @return the url string
   */
  public String webSocketURL(boolean secure, String host) {
    return "ws" + (secure ? "s" : "") + "://" + host + this.url();
  }

  /**
   * Transform this call to a relative path.
   *
   * @param requestHeader used to identify the current URL to make this Call relative to.
   * @return the relative path string
   */
  public String relativeTo(Http.RequestHeader requestHeader) {
    return this.relativeTo(requestHeader.path());
  }

  /**
   * Transform this call to a relative path.
   *
   * @param startPath the URL to make this Call relative to.
   * @return the relative path string
   */
  public String relativeTo(String startPath) {
    return Paths.relative(startPath, this.url()) + this.appendFragment();
  }

  /**
   * Transform this path into its canonical form.
   *
   * @return the canonical path.
   */
  public String canonical() {
    return Paths.canonical(this.url()) + this.appendFragment();
  }

  public String path() {
    return this.url() + this.appendFragment();
  }

  @Override
  public String toString() {
    return this.path();
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.db;

import java.sql.Connection;
import java.util.Map;
import javax.sql.DataSource;

import com.typesafe.config.Config;

import com.typesafe.config.ConfigFactory;
import scala.runtime.AbstractFunction1;
import scala.runtime.BoxedUnit;

/** Default delegating implementation of the database API. */
public class DefaultDatabase implements Database {

  private final play.api.db.Database db;

  public DefaultDatabase(play.api.db.Database database) {
    this.db = database;
  }

  /**
   * Create a default HikariCP-backed database.
   *
   * @param name name for the db's underlying datasource
   * @param configuration the database's configuration
   */
  public DefaultDatabase(String name, Config configuration) {
    this(
        new play.api.db.PooledDatabase(
            name,
            new play.api.Configuration(
                configuration.withFallback(
                    ConfigFactory.defaultReference().getConfig("play.db.prototype")))));
  }

  /**
   * Create a default HikariCP-backed database.
   *
   * @param name name for the db's underlying datasource
   * @param config the db's configuration
   */
  public DefaultDatabase(String name, Map<String, ? extends Object> config) {
    this(
        new play.api.db.PooledDatabase(
            name,
            new play.api.Configuration(
                ConfigFactory.parseMap(config)
                    .withFallback(
                        ConfigFactory.defaultReference().getConfig("play.db.prototype")))));
  }

  @Override
  public String getName() {
    return db.name();
  }

  @Override
  public DataSource getDataSource() {
    return db.dataSource();
  }

  @Override
  public String getUrl() {
    return db.url();
  }

  @Override
  public Connection getConnection() {
    return db.getConnection();
  }

  @Override
  public Connection getConnection(boolean autocommit) {
    return db.getConnection(autocommit);
  }

  @Override
  public void withConnection(ConnectionRunnable block) {
    db.withConnection(connectionFunction(block));
  }

  @Override
  public <A> A withConnection(ConnectionCallable<A> block) {
    return db.withConnection(connectionFunction(block));
  }

  @Override
  public void withConnection(boolean autocommit, ConnectionRunnable block) {
    db.withConnection(autocommit, connectionFunction(block));
  }

  @Override
  public <A> A withConnection(boolean autocommit, ConnectionCallable<A> block) {
    return db.withConnection(autocommit, connectionFunction(block));
  }

  @Override
  public void withTransaction(ConnectionRunnable block) {
    db.withTransaction(connectionFunction(block));
  }

  @Override
  public void withTransaction(TransactionIsolationLevel isolationLevel, ConnectionRunnable block) {
    db.withTransaction(isolationLevel.asScala(), connectionFunction(block));
  }

  @Override
  public <A> A withTransaction(ConnectionCallable<A> block) {
    return db.withTransaction(connectionFunction(block));
  }

  @Override
  public <A> A withTransaction(
      TransactionIsolationLevel isolationLevel, ConnectionCallable<A> block) {
    return db.withTransaction(isolationLevel.asScala(), connectionFunction(block));
  }

  @Override
  public void shutdown() {
    db.shutdown();
  }

  /**
   * Create a Scala function wrapper for ConnectionRunnable.
   *
   * @param block a Java functional interface instance to wrap
   * @return a scala function that wraps the given block
   */
  AbstractFunction1<Connection, BoxedUnit> connectionFunction(final ConnectionRunnable block) {
    return new AbstractFunction1<Connection, BoxedUnit>() {
      public BoxedUnit apply(Connection connection) {
        try {
          block.run(connection);
          return BoxedUnit.UNIT;
        } catch (java.sql.SQLException e) {
          throw new RuntimeException("Connection runnable failed", e);
        }
      }
    };
  }

  /**
   * Create a Scala function wrapper for ConnectionCallable.
   *
   * @param block a Java functional interface instance to wrap
   * @param <A> the provided block's return type
   * @return a scala function wrapping the given block
   */
  <A> AbstractFunction1<Connection, A> connectionFunction(final ConnectionCallable<A> block) {
    return new AbstractFunction1<Connection, A>() {
      public A apply(Connection connection) {
        try {
          return block.call(connection);
        } catch (java.sql.SQLException e) {
          throw new RuntimeException("Connection callable failed", e);
        }
      }
    };
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package javaguide.http.routing.controllers;

import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Http;

public class Application extends Controller {

  public Result download(String path) {
    return ok("download " + path);
  }

  public Result homePage() {
    return ok("home page");
  }

  // #show-page-action
  public Result show(String page) {
    String content = Page.getContentOf(page);
    return ok(content).as("text/html");
  }
  // #show-page-action

  static class Page {
    static String getContentOf(String page) {
      return "showing page " + page;
    }
  }

  // #reverse-redirect
  // Redirect to /hello/Bob
  public Result index() {
    return redirect(controllers.routes.Application.hello("Bob"));
  }
  // #reverse-redirect

  static class controllers {
    static javaguide.http.routing.reverse.controllers.routes routes =
        new javaguide.http.routing.reverse.controllers.routes();
  }

  // #pass-request
  public Result dashboard(Http.Request request) {
    return ok("Hello, your request path " + request.path());
  }
  // #pass-request
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.server;

import play.Mode;
import play.BuiltInComponents;
import play.routing.Router;
import play.core.j.JavaModeConverter;
import play.core.server.JavaServerHelper;

import java.net.InetSocketAddress;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import scala.compat.java8.OptionConverters;

/** A Play server. */
public class Server {

  private final play.core.server.Server server;

  public Server(play.core.server.Server server) {
    this.server = server;
  }

  /** @return the underlying server. */
  public play.core.server.Server underlying() {
    return this.server;
  }

  /** Stop the server. */
  public void stop() {
    server.stop();
  }

  /**
   * Get the HTTP port the server is running on.
   *
   * @throws IllegalStateException if it is not running on the HTTP protocol
   * @return the port number.
   */
  public int httpPort() {
    if (server.httpPort().isDefined()) {
      return (Integer) server.httpPort().get();
    } else {
      throw new IllegalStateException(
          "Server has no HTTP port. Try starting it with \"new Server.Builder().http(<port num>)\"?");
    }
  }

  /**
   * Get the HTTPS port the server is running on.
   *
   * @throws IllegalStateException if it is not running on the HTTPS protocol.
   * @return the port number.
   */
  public int httpsPort() {
    if (server.httpsPort().isDefined()) {
      return (Integer) server.httpsPort().get();
    } else {
      throw new IllegalStateException(
          "Server has no HTTPS port. Try starting it with \"new Server.Builder.https(<port num>)\"?");
    }
  }

  /**
   * Get the address the server is running on.
   *
   * @return the address
   */
  public InetSocketAddress mainAddress() {
    return server.mainAddress();
  }

  /**
   * Create a server for the given router.
   *
   *
The server will be running on a randomly selected ephemeral port, which can be checked using
   * the httpPort property.
   *
   *
The server will be running in TEST mode.
   *
   * @param block The block that creates the router.
   * @return The running server.
   */
  public static Server forRouter(Function<BuiltInComponents, Router> block) {
    return forRouter(Mode.TEST, 0, block);
  }

  /**
   * Create a server for the given router.
   *
   *
The server will be running on a randomly selected ephemeral port, which can be checked using
   * the httpPort property.
   *
   *
The server will be running in TEST mode.
   *
   * @param mode The mode the server will run on.
   * @param block The block that creates the router.
   * @return The running server.
   */
  public static Server forRouter(Mode mode, Function<BuiltInComponents, Router> block) {
    return forRouter(mode, 0, block);
  }

  /**
   * Create a server for the given router.
   *
   *
The server will be running on a randomly selected ephemeral port, which can be checked using
   * the httpPort property.
   *
   *
The server will be running in TEST mode.
   *
   * @param port The port the server will run on.
   * @param block The block that creates the router.
   * @return The running server.
   */
  public static Server forRouter(int port, Function<BuiltInComponents, Router> block) {
    return forRouter(Mode.TEST, port, block);
  }

  /**
   * Create a server for the router returned by the given block.
   *
   * @param block The block which creates a router.
   * @param mode The mode the server will run on.
   * @param port The port the server will run on.
   * @return The running server.
   */
  public static Server forRouter(Mode mode, int port, Function<BuiltInComponents, Router> block) {
    return new Builder().mode(mode).http(port).build(block);
  }

  /** Specifies the protocols supported by the server. */
  public enum Protocol {
    HTTP,
    HTTPS
  }

  private static class Config {
    private final Map<Protocol, Integer> _ports;
    private final Mode _mode;

    Config(Map<Protocol, Integer> _ports, Mode mode) {
      this._ports = _ports;
      this._mode = mode;
    }

    public Optional<Integer> maybeHttpPort() {
      return Optional.ofNullable(_ports.get(Protocol.HTTP));
    }

    public Optional<Integer> maybeHttpsPort() {
      return Optional.ofNullable(_ports.get(Protocol.HTTPS));
    }

    public Map<Protocol, Integer> ports() {
      return _ports;
    }

    public Mode mode() {
      return _mode;
    }
  }

  /**
   * Configures and builds an embedded server. If not further configured, it will default to serving
   * TEST mode over HTTP on a random available port.
   */
  public static class Builder {
    private Server.Config _config = new Server.Config(new EnumMap<>(Protocol.class), Mode.TEST);

    /**
     * Instruct the server to serve HTTP on a particular port.
     *
     *
Passing 0 will make it serve on a random available port.
     *
     * @param port the port on which to serve http traffic
     * @return the builder with port set.
     */
    public Builder http(int port) {
      return _protocol(Protocol.HTTP, port);
    }

    /**
     * Configure the server to serve HTTPS on a particular port.
     *
     *
Passing 0 will make it serve on a random available port.
     *
     * @param port the port on which to serve ssl traffic
     * @return the builder with port set.
     */
    public Builder https(int port) {
      return _protocol(Protocol.HTTPS, port);
    }

    /**
     * Set the mode the server should be run on (defaults to TEST)
     *
     * @param mode the Play mode (dev, prod, test)
     * @return the builder with Server.Config set to mode.
     */
    public Builder mode(Mode mode) {
      _config = new Server.Config(_config.ports(), mode);
      return this;
    }

    /**
     * Build the server and begin serving the provided routes as configured.
     *
     * @param router the router to use.
     * @return the actively running server.
     */
    public Server build(final Router router) {
      return build((components) -> router);
    }

    /**
     * Build the server and begin serving the provided routes as configured.
     *
     * @param block the router to use.
     * @return the actively running server.
     */
    public Server build(Function<BuiltInComponents, Router> block) {
      Server.Config config = _buildConfig();
      return new Server(
          JavaServerHelper.forRouter(
              JavaModeConverter.asScalaMode(config.mode()),
              OptionConverters.toScala(config.maybeHttpPort()),
              OptionConverters.toScala(config.maybeHttpsPort()),
              block));
    }

    //
    // Private members
    //
    private Server.Config _buildConfig() {
      Builder builder = this;
      if (_config.ports().isEmpty()) {
        builder = this._protocol(Protocol.HTTP, 0);
      }

      return builder._config;
    }

    private Builder _protocol(Protocol protocol, int port) {
      Map<Protocol, Integer> newPorts = new EnumMap<>(Protocol.class);
      newPorts.putAll(_config.ports());
      newPorts.put(protocol, port);

      _config = new Server.Config(newPorts, _config.mode());

      return this;
    }
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package javaguide.binder.controllers;

// #javascript-router-resource-imports
import play.mvc.Http;
import play.routing.JavaScriptReverseRouter;
import play.mvc.Controller;
import play.mvc.Result;
// #javascript-router-resource-imports

public class Application extends Controller {

  // #javascript-router-resource
  public Result javascriptRoutes(Http.Request request) {
    return ok(
        // ###skip: 1
        // TODO: After Play 2.7 use create(String name, String host, JavaScriptReverseRoute...
        // routes) instead
        JavaScriptReverseRouter.create(
            "jsRoutes",
            "jQuery.ajax",
            request.host(),
            routes.javascript.Users.list(),
            routes.javascript.Users.get()))
        .as(Http.MimeTypes.JAVASCRIPT);
  }
  // #javascript-router-resource

  public Result javascriptRoutes2(Http.Request request) {
    return ok(
        // #javascript-router-resource-custom-method
        JavaScriptReverseRouter.create(
            "jsRoutes",
            "myAjaxMethod",
            request.host(),
            routes.javascript.Users.list(),
            routes.javascript.Users.get())
        // #javascript-router-resource-custom-method
        )
        .as(Http.MimeTypes.JAVASCRIPT);
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.libs;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

/** Helper functions to handle JsonNode values. */
public class Json {
  private static final ObjectMapper defaultObjectMapper = newDefaultMapper();
  private static volatile ObjectMapper objectMapper = null;

  public static ObjectMapper newDefaultMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Jdk8Module());
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    return mapper;
  }

  /**
   * Gets the ObjectMapper used to serialize and deserialize objects to and from JSON values.
   *
   *
This can be set to a custom implementation using Json.setObjectMapper.
   *
   * @return the ObjectMapper currently being used
   */
  public static ObjectMapper mapper() {
    if (objectMapper == null) {
      return defaultObjectMapper;
    } else {
      return objectMapper;
    }
  }

  private static String generateJson(Object o, boolean prettyPrint, boolean escapeNonASCII) {
    try {
      ObjectWriter writer = mapper().writer();
      if (prettyPrint) {
        writer = writer.with(SerializationFeature.INDENT_OUTPUT);
      }
      if (escapeNonASCII) {
        writer = writer.with(Feature.ESCAPE_NON_ASCII);
      }
      return writer.writeValueAsString(o);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Converts an object to JsonNode.
   *
   * @param data Value to convert in Json.
   * @return the JSON node.
   */
  public static JsonNode toJson(final Object data) {
    try {
      return mapper().valueToTree(data);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Converts a JsonNode to a Java value
   *
   * @param  the type of the return value.
   * @param json Json value to convert.
   * @param clazz Expected Java value type.
   * @return the return value.
   */
  public static  A fromJson(JsonNode json, Class clazz) {
    try {
      return mapper().treeToValue(json, clazz);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Creates a new empty ObjectNode.
   *
   * @return new empty ObjectNode.
   */
  public static ObjectNode newObject() {
    return mapper().createObjectNode();
  }

  /**
   * Creates a new empty ArrayNode.
   *
   * @return a new empty ArrayNode.
   */
  public static ArrayNode newArray() {
    return mapper().createArrayNode();
  }

  /**
   * Converts a JsonNode to its string representation.
   *
   * @param json the JSON node to convert.
   * @return the string representation.
   */
  public static String stringify(JsonNode json) {
    return generateJson(json, false, false);
  }

  /**
   * Converts a JsonNode to its string representation, escaping non-ascii characters.
   *
   * @param json the JSON node to convert.
   * @return the string representation with escaped non-ascii characters.
   */
  public static String asciiStringify(JsonNode json) {
    return generateJson(json, false, true);
  }

  /**
   * Converts a JsonNode to its string representation.
   *
   * @param json the JSON node to convert.
   * @return the string representation, pretty printed.
   */
  public static String prettyPrint(JsonNode json) {
    return generateJson(json, true, false);
  }

  /**
   * Parses a String representing a json, and return it as a JsonNode.
   *
   * @param src the JSON string.
   * @return the JSON node.
   */
  public static JsonNode parse(String src) {
    try {
      return mapper().readTree(src);
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }

  /**
   * Parses a InputStream representing a json, and return it as a JsonNode.
   *
   * @param src the JSON input stream.
   * @return the JSON node.
   */
  public static JsonNode parse(java.io.InputStream src) {
    try {
      return mapper().readTree(src);
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }

  /**
   * Parses a byte array representing a json, and return it as a JsonNode.
   *
   * @param src the JSON input bytes.
   * @return the JSON node.
   */
  public static JsonNode parse(byte[] src) {
    try {
      return mapper().readTree(src);
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }

  /**
   * Inject the object mapper to use.
   *
   *
This is intended to be used when Play starts up. By default, Play will inject its own object
   * mapper here, but this mapper can be overridden either by a custom module.
   *
   * @param mapper the object mapper.
   */
  public static void setObjectMapper(ObjectMapper mapper) {
    objectMapper = mapper;
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.libs.crypto;

import javax.inject.Inject;
import javax.inject.Singleton;

/** This class delegates to the Scala CookieSigner. */
@Singleton
public class DefaultCookieSigner implements CookieSigner {

  private final play.api.libs.crypto.CookieSigner signer;

  @Inject
  public DefaultCookieSigner(play.api.libs.crypto.CookieSigner signer) {
    this.signer = signer;
  }

  /**
   * Signs the given String using the application's secret key.
   *
   * @param message The message to sign.
   * @return A hexadecimal encoded signature.
   */
  @Override
  public String sign(String message) {
    return signer.sign(message);
  }

  /**
   * Signs the given String using the given key. <br>
   *
   * @param message The message to sign.
   * @param key The private key to sign with.
   * @return A hexadecimal encoded signature.
   */
  @Override
  public String sign(String message, byte[] key) {
    return signer.sign(message, key);
  }

  @Override
  public play.api.libs.crypto.CookieSigner asScala() {
    return this.signer;
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.routing;

import play.api.routing.JavaScriptReverseRoute;
import play.libs.Scala;
import play.twirl.api.JavaScript;

/** Helpers for creating JavaScript reverse routers */
public class JavaScriptReverseRouter {

  /**
   * Generates a JavaScript reverse router.
   *
   * @param name the router's name
   * @param ajaxMethod which asynchronous call method the user's browser will use (e.g.
   *     "jQuery.ajax")
   * @param host the host to use for the reverse route
   * @param routes the reverse routes for this router
   * @return the router
   */
  public static JavaScript create(
      String name, String ajaxMethod, String host, JavaScriptReverseRoute... routes) {
    return play.api.routing.JavaScriptReverseRouter.apply(
        name, Scala.Option(ajaxMethod), host, Scala.varargs(routes));
  }

  /**
   * Generates a JavaScript reverse router.
   *
   * @param name the router's name
   * @param ajaxMethod which asynchronous call method the user's browser will use (e.g.
   *     "jQuery.ajax")
   * @param routes the reverse routes for this router
   * @return the router
   * @deprecated Deprecated as of 2.7.0. Use {@link #create(String, String, String,
   *     JavaScriptReverseRoute...)} instead.
   */
  @Deprecated
  public static JavaScript create(
      String name, String ajaxMethod, JavaScriptReverseRoute... routes) {
    return create(name, ajaxMethod, play.mvc.Http.Context.current().request().host(), routes);
  }
  // TODO:
  // After removing the above create(String, String, JavaScriptReverseRoute...) method we can
  // instead add:
  // public static JavaScript create(String name, String host, JavaScriptReverseRoute... routes) {
  // ... }
  // (Right now they are ambiguous)

  /**
   * Generates a JavaScript reverse router.
   *
   * @param name the router's name
   * @param routes the reverse routes for this router
   * @return the router
   * @deprecated Deprecated as of 2.7.0. Use {@link #create(String, String, String,
   *     JavaScriptReverseRoute...)} instead.
   */
  @Deprecated
  public static JavaScript create(String name, JavaScriptReverseRoute... routes) {
    return create(name, "jQuery.ajax", routes);
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.libs;

import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/** Annotation utilities. */
public class AnnotationUtils {

  /**
   * Returns a new array whose entries do not contain container annotations anymore but the
   * indirectly present annotation(s) a container annotation was wrapping instead. An annotation is
   * considered a container annotation if its indirectly present annotation(s) are annotated with
   * {@link Repeatable}. Annotations inside the given array which don't meet the above definition of
   * a container annotations will be returned untouched.
   *
   * @param annotations An array of annotations to unwrap. Can contain both container and non
   *     container annotations.
   * @return A new array without container annotations but the container annotations' indirectly
   *     defined annotations.
   */
  public static <A extends Annotation> Annotation[] unwrapContainerAnnotations(
      final A[] annotations) {
    final List<Annotation> unwrappedAnnotations = new LinkedList<>();
    for (final Annotation maybeContainerAnnotation : annotations) {
      final List<Annotation> indirectlyPresentAnnotations =
          getIndirectlyPresentAnnotations(maybeContainerAnnotation);
      if (!indirectlyPresentAnnotations.isEmpty()) {
        unwrappedAnnotations.addAll(indirectlyPresentAnnotations);
      } else {
        unwrappedAnnotations.add(maybeContainerAnnotation); // was not a container annotation
      }
    }
    return unwrappedAnnotations.toArray(new Annotation[unwrappedAnnotations.size()]);
  }

  /**
   * If the return type of an existing {@code value()} method of the passed annotation is an {@code
   * Annotation[]} array and the annotations inside that {@code Annotation[]} array are annotated
   * with the {@link Repeatable} annotation the annotations of that array will be returned. If the
   * passed annotation does not have a {@code value()} method or the above criteria are not met an
   * empty list will be returned instead.
   *
   * @param maybeContainerAnnotation The annotation which {@code value()} method will be checked for
   *     other annotations
   * @return The annotations defined by the {@code value()} method or an empty list.
   */
  public static <A extends Annotation> List<Annotation> getIndirectlyPresentAnnotations(
      final A maybeContainerAnnotation) {
    try {
      final Method method = maybeContainerAnnotation.annotationType().getMethod("value");
      final Object o = method.invoke(maybeContainerAnnotation);
      if (Annotation[].class.isAssignableFrom(o.getClass())) {
        final Annotation[] indirectAnnotations = (Annotation[]) o;
        if (indirectAnnotations.length > 0
            && indirectAnnotations[0].annotationType().isAnnotationPresent(Repeatable.class)) {
          return Arrays.asList(indirectAnnotations);
        }
      }
    } catch (final NoSuchMethodException e) {
      // That's ok, this just wasn't a container annotation -> continue
    } catch (final SecurityException
        | IllegalAccessException
        | IllegalArgumentException
        | InvocationTargetException e) {
      throw new IllegalStateException(e);
    }
    return Collections.emptyList();
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Stack;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Implementations to work with URL paths. This is a utility class with usages by {@link
 * play.mvc.Call}.
 */
public final class Paths {
  private Paths() {}

  private static String CURRENT_DIR = ".";
  private static String SEPARATOR = "/";
  private static String PARENT_DIR = "..";

  /** Create a path to targetPath that's relative to the given startPath. */
  public static String relative(String startPath, String targetPath) {
    // If the start and target path's are the same then link to the current directory
    if (startPath.equals(targetPath)) {
      return CURRENT_DIR;
    }

    String[] start = toSegments(canonical(startPath));
    String[] target = toSegments(canonical(targetPath));

    // If start path has no trailing separator (a "file" path), then drop file segment
    if (!startPath.endsWith(SEPARATOR)) start = Arrays.copyOfRange(start, 0, start.length - 1);

    // If target path has no trailing separator, then drop file segment, but keep a reference to add
    // it later
    String targetFile = "";
    if (!targetPath.endsWith(SEPARATOR)) {
      targetFile = target[target.length - 1];
      target = Arrays.copyOfRange(target, 0, target.length - 1);
    }

    // Work out how much of the filepath is shared by start and path.
    String[] common = commonPrefix(start, target);
    String[] parents = toParentDirs(start.length - common.length);

    int relativeStartIdx = common.length;
    String[] relativeDirs = Arrays.copyOfRange(target, relativeStartIdx, target.length);
    String[] relativePath = Arrays.copyOf(parents, parents.length + relativeDirs.length);
    System.arraycopy(relativeDirs, 0, relativePath, parents.length, relativeDirs.length);

    // If this is not a sibling reference append a trailing / to path
    String trailingSep = "";
    if (relativePath.length > 0) trailingSep = SEPARATOR;

    return Arrays.stream(relativePath).collect(Collectors.joining(SEPARATOR))
        + trailingSep
        + targetFile;
  }

  /**
   * Create a canonical path that does not contain parent directories, current directories, or
   * superfluous directory separators.
   */
  public static String canonical(String url) {
    String[] urlPath = toSegments(url);
    Stack<String> canonical = new Stack<>();
    for (String comp : urlPath) {
      if (comp.isEmpty() || comp.equals(CURRENT_DIR)) continue;
      if (!comp.equals(PARENT_DIR) || (!canonical.empty() && canonical.peek().equals(PARENT_DIR)))
        canonical.push(comp);
      else canonical.pop();
    }

    String prefixSep = url.startsWith(SEPARATOR) ? SEPARATOR : "";
    String trailingSep = url.endsWith(SEPARATOR) ? SEPARATOR : "";

    return prefixSep + canonical.stream().collect(Collectors.joining(SEPARATOR)) + trailingSep;
  }

  private static String[] toSegments(String url) {
    return Arrays.stream(url.split(SEPARATOR)).filter(s -> !s.isEmpty()).toArray(String[]::new);
  }

  private static String[] toParentDirs(int count) {
    return IntStream.range(0, count).mapToObj(i -> PARENT_DIR).toArray(String[]::new);
  }

  private static String[] commonPrefix(String[] path1, String[] path2) {
    int minLength = path1.length < path2.length ? path1.length : path2.length;

    ArrayList<String> match = new ArrayList<>();
    for (int i = 0; i < minLength; i++)
      if (!path1[i].equals(path2[i])) break;
      else match.add(path1[i]);

    return match.toArray(new String[0]);
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package javaguide.hello;

import play.mvc.*;

public class HelloController extends Controller {

  // #hello-world-index-action
  public Result index() {
    // ###replace:    ok(views.html.index.render());
    return ok(javaguide.hello.html.index.render());
  }
  // #hello-world-index-action

  // #hello-world-hello-action
  public Result hello() {
    // ###replace:     return ok(views.html.hello.render());
    return ok(javaguide.hello.html.hello.render());
  }
  // #hello-world-hello-action

  /*
  //#hello-world-hello-error-action
  public Result hello(String name) {
      return ok(views.html.hello.render());
  }
  //#hello-world-hello-error-action
   */

  // #hello-world-hello-correct-action
  public Result hello(String name) {
    // ###replace:    return ok(views.html.hello.render(name));
    return ok(javaguide.hello.html.helloName.render(name));
  }
  // #hello-world-hello-correct-action
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.data.validation;

import com.typesafe.config.Config;

import play.i18n.Lang;
import play.i18n.Messages;
import play.data.Form.Display;

import static play.libs.F.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.*;
import java.lang.reflect.Constructor;

import javax.validation.*;
import javax.validation.metadata.*;

import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import play.libs.typedmap.TypedMap;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** Defines a set of built-in validation constraints. */
public class Constraints {

  /** Super-type for validators. */
  public abstract static class Validator<T> {

    /**
     * @param object the value to test.
     * @return {@code true} if this value is valid.
     */
    public abstract boolean isValid(T object);

    /**
     * @param object the object to check
     * @param constraintContext The JSR-303 validation context.
     * @return {@code true} if this value is valid for the given constraint.
     */
    public boolean isValid(T object, ConstraintValidatorContext constraintContext) {
      return isValid(object);
    }

    public abstract Tuple<String, Object[]> getErrorMessageKey();
  }

  /** Super-type for validators with a payload. */
  public abstract static class ValidatorWithPayload<T> {

    /**
     * @param object the value to test.
     * @param payload the payload providing validation context information.
     * @return {@code true} if this value is valid.
     */
    public abstract boolean isValid(T object, ValidationPayload payload);

    /**
     * @param object the object to check
     * @param constraintContext The JSR-303 validation context.
     * @return {@code true} if this value is valid for the given constraint.
     */
    public boolean isValid(T object, ConstraintValidatorContext constraintContext) {
      return isValid(
          object,
          constraintContext
              .unwrap(HibernateConstraintValidatorContext.class)
              .getConstraintValidatorPayload(ValidationPayload.class));
    }

    public abstract Tuple<String, Object[]> getErrorMessageKey();
  }

  public static class ValidationPayload {
    private final Lang lang;
    private final Messages messages;
    private final Map<String, Object> args;
    private final TypedMap attrs;
    private final Config config;

    public ValidationPayload(
        final Lang lang, final Messages messages, final TypedMap attrs, final Config config) {
      this(lang, messages, Collections.emptyMap(), attrs, config);
    }

    /**
     * @deprecated Deprecated as of 2.7.0. Use {@link #ValidationPayload(Lang, Messages, TypedMap,
     *     Config)} instead.
     */
    @Deprecated
    public ValidationPayload(
        final Lang lang,
        final Messages messages,
        final Map<String, Object> args,
        final TypedMap attrs,
        final Config config) {
      this.lang = lang;
      this.messages = messages;
      this.args = args;
      this.attrs = attrs;
      this.config = config;
    }

    /**
     * @return if validation happens during a Http Request the lang of that request, otherwise null
     */
    public Lang getLang() {
      return this.lang;
    }

    /**
     * @return if validation happens during a Http Request the messages for the lang of that
     *     request, otherwise null
     */
    public Messages getMessages() {
      return this.messages;
    }

    /**
     * @return if validation happens during a Http Request the args map of that request, otherwise
     *     null
     * @deprecated Use {@link #getAttrs()} instead. Since 2.7.0.
     */
    @Deprecated
    public Map<String, Object> getArgs() {
      return this.args;
    }

    /**
     * @return if validation happens during a Http Request the request attributes of that request,
     *     otherwise null
     */
    public TypedMap getAttrs() {
      return this.attrs;
    }

    /**
     * @return the current application configuration, will always be set, even when accessed outside
     *     a Http Request
     */
    public Config getConfig() {
      return this.config;
    }
  }

  /**
   * Converts a set of constraints to human-readable values. Does not guarantee the order of the
   * returned constraints.
   *
   *
This method calls {@code displayableConstraint} under the hood.
   *
   * @param constraints the set of constraint descriptors.
   * @return a list of pairs of tuples assembled from displayableConstraint.
   */
  public static List<Tuple<String, List<Object>>> displayableConstraint(
      Set<ConstraintDescriptor<?>> constraints) {
    return constraints
        .parallelStream()
        .filter(c -> c.getAnnotation().annotationType().isAnnotationPresent(Display.class))
        .map(c -> displayableConstraint(c))
        .collect(Collectors.toList());
  }

  /**
   * Converts a set of constraints to human-readable values in guaranteed order. Only constraints
   * that have an annotation that intersect with the {@code orderedAnnotations} parameter will be
   * considered. The order of the returned constraints corresponds to the order of the {@code
   * orderedAnnotations parameter}.
   *
   * @param constraints the set of constraint descriptors.
   * @param orderedAnnotations the array of annotations
   * @return a list of tuples showing readable constraints.
   */
  public static List<Tuple<String, List<Object>>> displayableConstraint(
      Set<ConstraintDescriptor<?>> constraints, Annotation[] orderedAnnotations) {
    final List<Annotation> constraintAnnot =
        constraints.stream().map(c -> c.getAnnotation()).collect(Collectors.<Annotation>toList());

    return Stream.of(orderedAnnotations)
        .filter(
            constraintAnnot
                ::contains) // only use annotations for which we actually have a constraint
        .filter(a -> a.annotationType().isAnnotationPresent(Display.class))
        .map(
            a ->
                displayableConstraint(
                    constraints
                        .parallelStream()
                        .filter(c -> c.getAnnotation().equals(a))
                        .findFirst()
                        .get()))
        .collect(Collectors.toList());
  }

  /**
   * Converts a constraint to a human-readable value.
   *
   * @param constraint the constraint descriptor.
   * @return A tuple containing the constraint's display name and the constraint attributes.
   */
  public static Tuple<String, List<Object>> displayableConstraint(
      ConstraintDescriptor<?> constraint) {
    final Display displayAnnotation =
        constraint.getAnnotation().annotationType().getAnnotation(Display.class);
    return Tuple(
        displayAnnotation.name(),
        Collections.unmodifiableList(
            Stream.of(displayAnnotation.attributes())
                .map(attr -> constraint.getAttributes().get(attr))
                .collect(Collectors.toList())));
  }

  // --- Required

  /** Defines a field as required. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = RequiredValidator.class)
  @Repeatable(play.data.validation.Constraints.Required.List.class)
  @Display(name = "constraint.required")
  public @interface Required {
    String message() default RequiredValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /** Defines several {@code @Required} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      Required[] value();
    }
  }

  /** Validator for {@code @Required} fields. */
  public static class RequiredValidator extends Validator<Object>
      implements ConstraintValidator<Required, Object> {

    public static final String message = "error.required";

    public void initialize(Required constraintAnnotation) {}

    public boolean isValid(Object object) {
      if (object == null) {
        return false;
      }

      if (object instanceof String) {
        return !((String) object).isEmpty();
      }

      if (object instanceof Collection) {
        return !((Collection) object).isEmpty();
      }

      return true;
    }

    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {});
    }
  }

  /**
   * Constructs a 'required' validator.
   *
   * @return the RequiredValidator
   */
  public static Validator<Object> required() {
    return new RequiredValidator();
  }

  // --- Min

  /** Defines a minimum value for a numeric field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = MinValidator.class)
  @Repeatable(play.data.validation.Constraints.Min.List.class)
  @Display(
      name = "constraint.min",
      attributes = {"value"})
  public @interface Min {
    String message() default MinValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    long value();

    /** Defines several {@code @Min} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      Min[] value();
    }
  }

  /** Validator for {@code @Min} fields. */
  public static class MinValidator extends Validator<Number>
      implements ConstraintValidator<Min, Number> {

    public static final String message = "error.min";
    private long min;

    public MinValidator() {}

    public MinValidator(long value) {
      this.min = value;
    }

    public void initialize(Min constraintAnnotation) {
      this.min = constraintAnnotation.value();
    }

    public boolean isValid(Number object) {
      if (object == null) {
        return true;
      }

      return object.longValue() >= min;
    }

    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {min});
    }
  }

  /**
   * Constructs a 'min' validator.
   *
   * @param value the minimum value
   * @return a validator for number.
   */
  public static Validator<Number> min(long value) {
    return new MinValidator(value);
  }

  // --- Max

  /** Defines a maximum value for a numeric field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = MaxValidator.class)
  @Repeatable(play.data.validation.Constraints.Max.List.class)
  @Display(
      name = "constraint.max",
      attributes = {"value"})
  public @interface Max {
    String message() default MaxValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    long value();

    /** Defines several {@code @Max} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      Max[] value();
    }
  }

  /** Validator for @Max fields. */
  public static class MaxValidator extends Validator<Number>
      implements ConstraintValidator<Max, Number> {

    public static final String message = "error.max";
    private long max;

    public MaxValidator() {}

    public MaxValidator(long value) {
      this.max = value;
    }

    public void initialize(Max constraintAnnotation) {
      this.max = constraintAnnotation.value();
    }

    public boolean isValid(Number object) {
      if (object == null) {
        return true;
      }

      return object.longValue() <= max;
    }

    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {max});
    }
  }

  /**
   * Constructs a 'max' validator.
   *
   * @param value maximum value
   * @return a validator using MaxValidator.
   */
  public static Validator<Number> max(long value) {
    return new MaxValidator(value);
  }

  // --- MinLength

  /** Defines a minimum length for a string field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = MinLengthValidator.class)
  @Repeatable(play.data.validation.Constraints.MinLength.List.class)
  @Display(
      name = "constraint.minLength",
      attributes = {"value"})
  public @interface MinLength {
    String message() default MinLengthValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    long value();

    /** Defines several {@code @MinLength} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      MinLength[] value();
    }
  }

  /** Validator for {@code @MinLength} fields. */
  public static class MinLengthValidator extends Validator<String>
      implements ConstraintValidator<MinLength, String> {

    public static final String message = "error.minLength";
    private long min;

    public MinLengthValidator() {}

    public MinLengthValidator(long value) {
      this.min = value;
    }

    public void initialize(MinLength constraintAnnotation) {
      this.min = constraintAnnotation.value();
    }

    public boolean isValid(String object) {
      if (object == null || object.isEmpty()) {
        return true;
      }

      return object.length() >= min;
    }

    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {min});
    }
  }

  /**
   * Constructs a 'minLength' validator.
   *
   * @param value the minimum length value.
   * @return the MinLengthValidator
   */
  public static Validator<String> minLength(long value) {
    return new MinLengthValidator(value);
  }

  // --- MaxLength

  /** Defines a maximum length for a string field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = MaxLengthValidator.class)
  @Repeatable(play.data.validation.Constraints.MaxLength.List.class)
  @Display(
      name = "constraint.maxLength",
      attributes = {"value"})
  public @interface MaxLength {
    String message() default MaxLengthValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    long value();

    /** Defines several {@code @MaxLength} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      MaxLength[] value();
    }
  }

  /** Validator for {@code @MaxLength} fields. */
  public static class MaxLengthValidator extends Validator<String>
      implements ConstraintValidator<MaxLength, String> {

    public static final String message = "error.maxLength";
    private long max;

    public MaxLengthValidator() {}

    public MaxLengthValidator(long value) {
      this.max = value;
    }

    public void initialize(MaxLength constraintAnnotation) {
      this.max = constraintAnnotation.value();
    }

    public boolean isValid(String object) {
      if (object == null || object.isEmpty()) {
        return true;
      }

      return object.length() <= max;
    }

    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {max});
    }
  }

  /**
   * Constructs a 'maxLength' validator.
   *
   * @param value the max length
   * @return the MaxLengthValidator
   */
  public static Validator<String> maxLength(long value) {
    return new MaxLengthValidator(value);
  }

  // --- Email

  /** Defines a email constraint for a string field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = EmailValidator.class)
  @Repeatable(play.data.validation.Constraints.Email.List.class)
  @Display(
      name = "constraint.email",
      attributes = {})
  public @interface Email {
    String message() default EmailValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /** Defines several {@code @Email} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      Email[] value();
    }
  }

  /** Validator for {@code @Email} fields. */
  public static class EmailValidator extends Validator<String>
      implements ConstraintValidator<Email, String> {

    public static final String message = "error.email";
    static final java.util.regex.Pattern regex =
        java.util.regex.Pattern.compile(
            "\\b[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\\b");

    public EmailValidator() {}

    @Override
    public void initialize(Email constraintAnnotation) {}

    @Override
    public boolean isValid(String object) {
      if (object == null || object.isEmpty()) {
        return true;
      }

      return regex.matcher(object).matches();
    }

    @Override
    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {});
    }
  }

  /**
   * Constructs a 'email' validator.
   *
   * @return the EmailValidator
   */
  public static Validator<String> email() {
    return new EmailValidator();
  }

  // --- Pattern

  /** Defines a pattern constraint for a string field. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = PatternValidator.class)
  @Repeatable(play.data.validation.Constraints.Pattern.List.class)
  @Display(
      name = "constraint.pattern",
      attributes = {"value"})
  public @interface Pattern {
    String message() default PatternValidator.message;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String value();

    /** Defines several {@code @Pattern} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      Pattern[] value();
    }
  }

  /** Validator for {@code @Pattern} fields. */
  public static class PatternValidator extends Validator<String>
      implements ConstraintValidator<Pattern, String> {

    public static final String message = "error.pattern";
    java.util.regex.Pattern regex = null;

    public PatternValidator() {}

    public PatternValidator(String regex) {
      this.regex = java.util.regex.Pattern.compile(regex);
    }

    @Override
    public void initialize(Pattern constraintAnnotation) {
      regex = java.util.regex.Pattern.compile(constraintAnnotation.value());
    }

    @Override
    public boolean isValid(String object) {
      if (object == null || object.isEmpty()) {
        return true;
      }

      return regex.matcher(object).matches();
    }

    @Override
    public Tuple<String, Object[]> getErrorMessageKey() {
      return Tuple(message, new Object[] {regex});
    }
  }

  /**
   * Constructs a 'pattern' validator.
   *
   * @param regex the regular expression to match.
   * @return the PatternValidator.
   */
  public static Validator<String> pattern(String regex) {
    return new PatternValidator(regex);
  }

  // --- validate fields with custom validator

  /** Defines a custom validator. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = ValidateWithValidator.class)
  @Repeatable(play.data.validation.Constraints.ValidateWith.List.class)
  @Display(
      name = "constraint.validatewith",
      attributes = {})
  public @interface ValidateWith {
    String message() default ValidateWithValidator.defaultMessage;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    Class<? extends Validator> value();

    /** Defines several {@code @ValidateWith} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      ValidateWith[] value();
    }
  }

  /** Validator for {@code @ValidateWith} fields. */
  public static class ValidateWithValidator extends Validator<Object>
      implements ConstraintValidator<ValidateWith, Object> {

    public static final String defaultMessage = "error.invalid";
    Class<?> clazz = null;
    Validator validator = null;

    public ValidateWithValidator() {}

    public ValidateWithValidator(Class clazz) {
      this.clazz = clazz;
    }

    public void initialize(ValidateWith constraintAnnotation) {
      this.clazz = constraintAnnotation.value();
      try {
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        validator = (Validator) constructor.newInstance();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    @SuppressWarnings("unchecked")
    public boolean isValid(Object object) {
      try {
        return validator.isValid(object);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    @SuppressWarnings("unchecked")
    public Tuple<String, Object[]> getErrorMessageKey() {
      Tuple<String, Object[]> errorMessageKey = null;
      try {
        errorMessageKey = validator.getErrorMessageKey();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      return (errorMessageKey != null) ? errorMessageKey : Tuple(defaultMessage, new Object[] {});
    }
  }

  // --- validate fields with custom validator that gets payload

  /** Defines a custom validator. */
  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = ValidatePayloadWithValidator.class)
  @Repeatable(play.data.validation.Constraints.ValidatePayloadWith.List.class)
  @Display(
      name = "constraint.validatewith",
      attributes = {})
  public @interface ValidatePayloadWith {
    String message() default ValidatePayloadWithValidator.defaultMessage;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    Class<? extends ValidatorWithPayload> value();

    /** Defines several {@code @ValidatePayloadWith} annotations on the same element. */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface List {
      ValidatePayloadWith[] value();
    }
  }

  /** Validator for {@code @ValidatePayloadWith} fields. */
  public static class ValidatePayloadWithValidator extends ValidatorWithPayload<Object>
      implements ConstraintValidator<ValidatePayloadWith, Object> {

    public static final String defaultMessage = "error.invalid";
    Class<?> clazz = null;
    ValidatorWithPayload validator = null;

    public ValidatePayloadWithValidator() {}

    public ValidatePayloadWithValidator(Class clazz) {
      this.clazz = clazz;
    }

    public void initialize(ValidatePayloadWith constraintAnnotation) {
      this.clazz = constraintAnnotation.value();
      try {
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        validator = (ValidatorWithPayload) constructor.newInstance();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    @SuppressWarnings("unchecked")
    public boolean isValid(Object object, ValidationPayload payload) {
      try {
        return validator.isValid(object, payload);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    @SuppressWarnings("unchecked")
    public Tuple<String, Object[]> getErrorMessageKey() {
      Tuple<String, Object[]> errorMessageKey = null;
      try {
        errorMessageKey = validator.getErrorMessageKey();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      return (errorMessageKey != null) ? errorMessageKey : Tuple(defaultMessage, new Object[] {});
    }
  }

  // --- class level helpers

  @Target({TYPE, ANNOTATION_TYPE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = ValidateValidator.class)
  @Repeatable(play.data.validation.Constraints.Validate.List.class)
  public @interface Validate {
    String message() default "error.invalid";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /** Defines several {@code @Validate} annotations on the same element. */
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    public @interface List {
      Validate[] value();
    }
  }

  public interface Validatable<T> {
    T validate();
  }

  @Target({TYPE, ANNOTATION_TYPE})
  @Retention(RUNTIME)
  @Constraint(validatedBy = ValidateValidatorWithPayload.class)
  @Repeatable(play.data.validation.Constraints.ValidateWithPayload.List.class)
  public @interface ValidateWithPayload {
    String message() default "error.invalid";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /** Defines several {@code @ValidateWithPayload} annotations on the same element. */
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    public @interface List {
      ValidateWithPayload[] value();
    }
  }

  public interface ValidatableWithPayload<T> {
    T validate(ValidationPayload payload);
  }

  public static class ValidateValidator
      implements PlayConstraintValidator<Validate, Validatable<?>> {

    @Override
    public void initialize(final Validate constraintAnnotation) {}

    @Override
    public boolean isValid(
        final Validatable<?> value, final ConstraintValidatorContext constraintValidatorContext) {
      return reportValidationStatus(value.validate(), constraintValidatorContext);
    }
  }

  public static class ValidateValidatorWithPayload
      implements PlayConstraintValidatorWithPayload<
          ValidateWithPayload, ValidatableWithPayload<?>> {

    @Override
    public void initialize(final ValidateWithPayload constraintAnnotation) {}

    @Override
    public boolean isValid(
        final ValidatableWithPayload<?> value,
        final ValidationPayload payload,
        final ConstraintValidatorContext constraintValidatorContext) {
      return reportValidationStatus(value.validate(payload), constraintValidatorContext);
    }
  }

  public interface PlayConstraintValidator
      extends ConstraintValidator {

    default boolean validationSuccessful(final Object validationResult) {
      return validationResult == null
          || (validationResult instanceof List && ((List<?>) validationResult).isEmpty());
    }

    default boolean reportValidationStatus(
        final Object validationResult,
        final ConstraintValidatorContext constraintValidatorContext) {
      if (validationSuccessful(validationResult)) {
        return true;
      }
      constraintValidatorContext
          .unwrap(HibernateConstraintValidatorContext.class)
          .withDynamicPayload(validationResult);
      return false;
    }
  }

  public interface PlayConstraintValidatorWithPayload
      extends PlayConstraintValidator {

    @Override
    default boolean isValid(
        final T value, final ConstraintValidatorContext constraintValidatorContext) {
      return isValid(
          value,
          constraintValidatorContext
              .unwrap(HibernateConstraintValidatorContext.class)
              .getConstraintValidatorPayload(ValidationPayload.class),
          constraintValidatorContext);
    }

    boolean isValid(
        final T value,
        final ValidationPayload payload,
        final ConstraintValidatorContext constraintValidatorContext);
  }
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package javaguide.sql;

// #jpa-repository-api-inject
import play.db.jpa.JPAApi;

import javax.inject.*;
import javax.persistence.*;
import java.util.concurrent.*;

@Singleton
public class JPARepository {
  private JPAApi jpaApi;
  private DatabaseExecutionContext executionContext;

  @Inject
  public JPARepository(JPAApi api, DatabaseExecutionContext executionContext) {
    this.jpaApi = api;
    this.executionContext = executionContext;
  }
}
// #jpa-repository-api-inject

class JPARepositoryMethods {
  private JPAApi jpaApi;
  private DatabaseExecutionContext executionContext;

  @Inject
  public JPARepositoryMethods(JPAApi api, DatabaseExecutionContext executionContext) {
    this.jpaApi = api;
    this.executionContext = executionContext;
  }

  // #jpa-withTransaction-function
  public CompletionStage<Long> runningWithTransaction() {
    return CompletableFuture.supplyAsync(
        () -> {
          // lambda is an instance of Function<EntityManager, Long>
          return jpaApi.withTransaction(
              entityManager -> {
                Query query = entityManager.createNativeQuery("select max(age) from people");
                return (Long) query.getSingleResult();
              });
        },
        executionContext);
  }
  // #jpa-withTransaction-function

  // #jpa-withTransaction-consumer
  public CompletionStage<Void> runningWithRunnable() {
    // lambda is an instance of Consumer<EntityManager>
    return CompletableFuture.runAsync(
        () -> {
          jpaApi.withTransaction(
              entityManager -> {
                Query query =
                    entityManager.createNativeQuery("update people set active = 1 where age > 18");
                query.executeUpdate();
              });
        },
        executionContext);
  }
  // #jpa-withTransaction-consumer
}


/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.libs.concurrent;

import akka.Done;
import play.libs.Scala;
import scala.concurrent.duration.FiniteDuration;
import scala.runtime.BoxedUnit;

import javax.inject.Inject;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

import static java.util.Objects.requireNonNull;
import static scala.compat.java8.FutureConverters.toJava;

/**
 * The default implementation of the Futures trait. This provides an implementation that uses the
 * scheduler of the application's ActorSystem.
 */
public class DefaultFutures implements Futures {

  private final play.api.libs.concurrent.Futures delegate;

  @Inject
  public DefaultFutures(play.api.libs.concurrent.Futures delegate) {
    this.delegate = delegate;
  }

  /**
   * Creates a CompletionStage that returns either the input stage, or a futures.
   *
   * <p>Note that timeout is not the same as cancellation. Even in case of futures, the given
   * completion stage will still complete, even though that completed value is not returned.
   *
   * @param stage the input completion stage that may time out.
   * @param amount The amount (expressed with the corresponding unit).
   * @param unit The time Unit.
   * @param <A> the completion's result type.
   * @return either the completed future, or a completion stage that failed with futures.
   */
  @Override
  public <A> CompletionStage<A> timeout(
      final CompletionStage<A> stage, final long amount, final TimeUnit unit) {
    requireNonNull(stage, "Null stage");
    requireNonNull(unit, "Null unit");

    FiniteDuration duration = FiniteDuration.apply(amount, unit);
    return toJava(delegate.timeout(duration, Scala.asScalaWithFuture(() -> stage)));
  }

  /**
   * An alias for futures(stage, delay, unit) that uses a java.time.Duration.
   *
   * @param stage the input completion stage that may time out.
   * @param duration The duration after which there is a timeout.
   * @param <A> the completion stage that should be wrapped with a future.
   * @return the completion stage, or a completion stage that failed with futures.
   */
  @Override
  public <A> CompletionStage<A> timeout(final CompletionStage<A> stage, final Duration duration) {
    requireNonNull(stage, "Null stage");
    requireNonNull(duration, "Null duration");

    FiniteDuration finiteDuration =
        FiniteDuration.apply(duration.toMillis(), TimeUnit.MILLISECONDS);
    return toJava(delegate.timeout(finiteDuration, Scala.asScalaWithFuture(() -> stage)));
  }

  /**
   * Create a CompletionStage which, after a delay, will be redeemed with the result of a given
   * supplier. The supplier will be called after the delay.
   *
   * @param callable the input completion stage that is delayed.
   * @param amount The time to wait.
   * @param unit The units to use for the amount.
   * @param <A> the type of the completion's result.
   * @return the delayed CompletionStage wrapping supplier.
   */
  @Override
  public <A> CompletionStage<A> delayed(
      final Callable<CompletionStage<A>> callable, long amount, TimeUnit unit) {
    requireNonNull(callable, "Null callable");
    requireNonNull(amount, "Null amount");
    requireNonNull(unit, "Null unit");

    FiniteDuration duration = FiniteDuration.apply(amount, unit);
    return toJava(delegate.delayed(duration, Scala.asScalaWithFuture(callable)));
  }

  @Override
  public CompletionStage<Done> delay(Duration duration) {
    FiniteDuration finiteDuration =
        FiniteDuration.apply(duration.toMillis(), TimeUnit.MILLISECONDS);
    return toJava(delegate.delay(finiteDuration));
  }

  @Override
  public CompletionStage<Done> delay(long amount, TimeUnit unit) {
    FiniteDuration finiteDuration = FiniteDuration.apply(amount, unit);
    return toJava(delegate.delay(finiteDuration));
  }

  /**
   * Create a CompletionStage which, after a delay, will be redeemed with the result of a given
   * supplier. The supplier will be called after the delay.
   *
   * @param callable the input completion stage that is delayed.
   * @param duration to wait.
   * @param <A> the type of the completion's result.
   * @return the delayed CompletionStage wrapping supplier.
   */
  @Override
  public <A> CompletionStage<A> delayed(
      final Callable<CompletionStage<A>> callable, Duration duration) {
    requireNonNull(callable, "Null callable");
    requireNonNull(duration, "Null duration");

    FiniteDuration finiteDuration =
        FiniteDuration.apply(duration.toMillis(), TimeUnit.MILLISECONDS);
    return toJava(delegate.delayed(finiteDuration, Scala.asScalaWithFuture(callable)));
  }
}


/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package play.libs.reflect;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;

/** Imported from apache.commons.lang3 3.6 */
abstract class ClassUtils {

  public static int arrayGetLength(final Object array) {
    if (array == null) {
      return 0;
    }
    return Array.getLength(array);
  }

  /** An empty immutable {@code Class} array. */
  public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];

  /**
   * Checks if one {@code Class} can be assigned to a variable of another {@code Class}.
   *
   *
Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into
   * account widenings of primitive classes and {@code null}s.
   *
   *
Primitive widenings allow an int to be assigned to a long, float or double. This method
   * returns the correct result for these cases.
   *
   *
{@code Null} may be assigned to any reference type. This method will return {@code true} if
   * {@code null} is passed in and the toClass is non-primitive.
   *
   *
Specifically, this method tests whether the type represented by the specified {@code Class}
   * parameter can be converted to the type represented by this {@code Class} object via an identity
   * conversion widening primitive or widening reference conversion. See The Java Language Specification, sections
   * 5.1.1, 5.1.2 and 5.1.4 for details.
   *
   *
Since Lang 3.0, this method will default behavior for calculating
   * assignability between primitive and wrapper types corresponding to the running Java
   * version; i.e. autoboxing will be the default behavior in VMs running Java versions >= 1.5.
   *
   * @param cls the Class to check, may be null
   * @param toClass the Class to try to assign into, returns false if null
   * @return {@code true} if assignment possible
   */
  public static boolean isAssignable(Class<?> cls, Class<?> toClass) {
    return isAssignable(
        cls, toClass, /* actually play runs on VMs > 8 only so autoboxing is always true */ true);
  }

  /**
   * Checks if one {@code Class} can be assigned to a variable of another {@code Class}.
   *
   *
Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into
   * account widenings of primitive classes and {@code null}s.
   *
   *
Primitive widenings allow an int to be assigned to a long, float or double. This method
   * returns the correct result for these cases.
   *
   *
{@code Null} may be assigned to any reference type. This method will return {@code true} if
   * {@code null} is passed in and the toClass is non-primitive.
   *
   *
Specifically, this method tests whether the type represented by the specified {@code Class}
   * parameter can be converted to the type represented by this {@code Class} object via an identity
   * conversion widening primitive or widening reference conversion. See The Java Language Specification, sections
   * 5.1.1, 5.1.2 and 5.1.4 for details.
   *
   * @param cls the Class to check, may be null
   * @param toClass the Class to try to assign into, returns false if null
   * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
   * @return {@code true} if assignment possible
   */
  public static boolean isAssignable(Class<?> cls, Class<?> toClass, boolean autoboxing) {
    if (toClass == null) {
      return false;
    }
    // have to check for null, as isAssignableFrom doesn't
    if (cls == null) {
      return !toClass.isPrimitive();
    }
    // autoboxing:
    if (autoboxing) {
      if (cls.isPrimitive() && !toClass.isPrimitive()) {
        cls = primitiveToWrapper(cls);
        if (cls == null) {
          return false;
        }
      }
      if (toClass.isPrimitive() && !cls.isPrimitive()) {
        cls = wrapperToPrimitive(cls);
        if (cls == null) {
          return false;
        }
      }
    }
    if (cls.equals(toClass)) {
      return true;
    }
    if (cls.isPrimitive()) {
      if (toClass.isPrimitive() == false) {
        return false;
      }
      if (Integer.TYPE.equals(cls)) {
        return Long.TYPE.equals(toClass)
            || Float.TYPE.equals(toClass)
            || Double.TYPE.equals(toClass);
      }
      if (Long.TYPE.equals(cls)) {
        return Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass);
      }
      if (Boolean.TYPE.equals(cls)) {
        return false;
      }
      if (Double.TYPE.equals(cls)) {
        return false;
      }
      if (Float.TYPE.equals(cls)) {
        return Double.TYPE.equals(toClass);
      }
      if (Character.TYPE.equals(cls)) {
        return Integer.TYPE.equals(toClass)
            || Long.TYPE.equals(toClass)
            || Float.TYPE.equals(toClass)
            || Double.TYPE.equals(toClass);
      }
      if (Short.TYPE.equals(cls)) {
        return Integer.TYPE.equals(toClass)
            || Long.TYPE.equals(toClass)
            || Float.TYPE.equals(toClass)
            || Double.TYPE.equals(toClass);
      }
      if (Byte.TYPE.equals(cls)) {
        return Short.TYPE.equals(toClass)
            || Integer.TYPE.equals(toClass)
            || Long.TYPE.equals(toClass)
            || Float.TYPE.equals(toClass)
            || Double.TYPE.equals(toClass);
      }
      // should never get here
      return false;
    }
    return toClass.isAssignableFrom(cls);
  }

  /**
   * Checks if an array of Classes can be assigned to another array of Classes.
   *
   *
This method calls {@link #isAssignable(Class, Class) isAssignable} for each Class pair in
   * the input arrays. It can be used to check if a set of arguments (the first parameter) are
   * suitably compatible with a set of method parameter types (the second parameter).
   *
   *
Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into
   * account widenings of primitive classes and {@code null}s.
   *
   *
Primitive widenings allow an int to be assigned to a {@code long}, {@code float} or {@code
   * double}. This method returns the correct result for these cases.
   *
   *
{@code Null} may be assigned to any reference type. This method will return {@code true} if
   * {@code null} is passed in and the toClass is non-primitive.
   *
   *
Specifically, this method tests whether the type represented by the specified {@code Class}
   * parameter can be converted to the type represented by this {@code Class} object via an identity
   * conversion widening primitive or widening reference conversion. See The Java Language Specification, sections
   * 5.1.1, 5.1.2 and 5.1.4 for details.
   *
   * @param classArray the array of Classes to check, may be {@code null}
   * @param toClassArray the array of Classes to try to assign into, may be {@code null}
   * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
   * @return {@code true} if assignment possible
   */
  public static boolean isAssignable(
      Class<?>[] classArray, Class<?>[] toClassArray, boolean autoboxing) {
    if (arrayGetLength(classArray) != arrayGetLength(toClassArray)) {
      return false;
    }
    if (classArray == null) {
      classArray = EMPTY_CLASS_ARRAY;
    }
    if (toClassArray == null) {
      toClassArray = EMPTY_CLASS_ARRAY;
    }
    for (int i = 0; i < classArray.length; i++) {
      if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) {
        return false;
      }
    }
    return true;
  }

  /** Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. */
  private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<>();

  static {
    primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
    primitiveWrapperMap.put(Byte.TYPE, Byte.class);
    primitiveWrapperMap.put(Character.TYPE, Character.class);
    primitiveWrapperMap.put(Short.TYPE, Short.class);
    primitiveWrapperMap.put(Integer.TYPE, Integer.class);
    primitiveWrapperMap.put(Long.TYPE, Long.class);
    primitiveWrapperMap.put(Double.TYPE, Double.class);
    primitiveWrapperMap.put(Float.TYPE, Float.class);
    primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
  }

  /** Maps wrapper {@code Class}es to their corresponding primitive types. */
  private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap =
      new HashMap<Class<?>, Class<?>>();

  static {
    for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) {
      Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass);
      if (!primitiveClass.equals(wrapperClass)) {
        wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
      }
    }
  }

  /**
   * Converts the specified primitive Class object to its corresponding wrapper Class object.
   *
   *
NOTE: From v2.2, this method handles {@code Void.TYPE}, returning {@code Void.TYPE}.
   *
   * @param cls the class to convert, may be null
   * @return the wrapper class for {@code cls} or {@code cls} if {@code cls} is not a primitive.
   *     {@code null} if null input.
   * @since 2.1
   */
  static Class<?> primitiveToWrapper(final Class<?> cls) {
    Class<?> convertedClass = cls;
    if (cls != null && cls.isPrimitive()) {
      convertedClass = primitiveWrapperMap.get(cls);
    }
    return convertedClass;
  }

  /**
   * Converts the specified wrapper class to its corresponding primitive class.
   *
   *
This method is the counter part of {@code primitiveToWrapper()}. If the passed in class is a
   * wrapper class for a primitive type, this primitive type will be returned (e.g. {@code
   * Integer.TYPE} for {@code Integer.class}). For other classes, or if the parameter is
   * null, the return value is null.
   *
   * @param cls the class to convert, may be null
   * @return the corresponding primitive type if {@code cls} is a wrapper class, null
   *     otherwise
   * @see #primitiveToWrapper(Class)
   * @since 2.4
   */
  public static Class<?> wrapperToPrimitive(Class<?> cls) {
    return wrapperPrimitiveMap.get(cls);
  }
}