/*
 * 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.Method;
import java.lang.reflect.Modifier;

/** Imported from apache.commons.lang3 3.6 */
public class MethodUtils {

  /**
   * {@link MethodUtils} instances should NOT be constructed in standard programming. Instead, the
   * class should be used as {@code MethodUtils.getAccessibleMethod(method)}.
   *
   *
This constructor is {@code public} to permit tools that require a JavaBean instance to
   * operate.
   */
  public MethodUtils() {
    super();
  }

  /**
   * Returns an accessible method (that is, one that can be invoked via reflection) that implements
   * the specified Method. If no such method can be found, return {@code null}.
   *
   * @param method The method that we wish to call
   * @return The accessible method
   */
  public static Method getAccessibleMethod(Method method) {
    if (!MemberUtils.isAccessible(method)) {
      return null;
    }
    // If the declaring class is public, we are done
    final Class<?> cls = method.getDeclaringClass();
    if (Modifier.isPublic(cls.getModifiers())) {
      return method;
    }
    final String methodName = method.getName();
    final Class<?>[] parameterTypes = method.getParameterTypes();

    // Check the implemented interfaces and subinterfaces
    method = getAccessibleMethodFromInterfaceNest(cls, methodName, parameterTypes);

    // Check the superclass chain
    if (method == null) {
      method = getAccessibleMethodFromSuperclass(cls, methodName, parameterTypes);
    }
    return method;
  }

  /**
   * Returns an accessible method (that is, one that can be invoked via reflection) by scanning
   * through the superclasses. If no such method can be found, return {@code null}.
   *
   * @param cls Class to be checked
   * @param methodName Method name of the method we wish to call
   * @param parameterTypes The parameter type signatures
   * @return the accessible method or {@code null} if not found
   */
  private static Method getAccessibleMethodFromSuperclass(
      final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
    Class<?> parentClass = cls.getSuperclass();
    while (parentClass != null) {
      if (Modifier.isPublic(parentClass.getModifiers())) {
        try {
          return parentClass.getMethod(methodName, parameterTypes);
        } catch (final NoSuchMethodException e) {
          return null;
        }
      }
      parentClass = parentClass.getSuperclass();
    }
    return null;
  }

  /**
   * Returns an accessible method (that is, one that can be invoked via reflection) that implements
   * the specified method, by scanning through all implemented interfaces and subinterfaces. If no
   * such method can be found, return {@code null}.
   *
   *
There isn't any good reason why this method must be {@code private}. It is because there
   * doesn't seem any reason why other classes should call this rather than the higher level
   * methods.
   *
   * @param cls Parent class for the interfaces to be checked
   * @param methodName Method name of the method we wish to call
   * @param parameterTypes The parameter type signatures
   * @return the accessible method or {@code null} if not found
   */
  private static Method getAccessibleMethodFromInterfaceNest(
      Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
    // Search up the superclass chain
    for (; cls != null; cls = cls.getSuperclass()) {

      // Check the implemented interfaces of the parent class
      final Class<?>[] interfaces = cls.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        // Is this interface public?
        if (!Modifier.isPublic(anInterface.getModifiers())) {
          continue;
        }
        // Does the method exist on this interface?
        try {
          return anInterface.getDeclaredMethod(methodName, parameterTypes);
        } catch (final NoSuchMethodException e) { // NOPMD
          /*
           * Swallow, if no method is found after the loop then this
           * method returns null.
           */
        }
        // Recursively check our parent interfaces
        final Method method =
            getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
        if (method != null) {
          return method;
        }
      }
    }
    return null;
  }

  /**
   * Finds an accessible method that matches the given name and has compatible parameters.
   * Compatible parameters mean that every method parameter is assignable from the given parameters.
   * In other words, it finds a method with the given name that will take the parameters given.
   *
   *
This method can match primitive parameter by passing in wrapper classes. For example, a
   * {@code Boolean} will match a primitive {@code boolean} parameter.
   *
   * @param cls find method in this class
   * @param methodName find method with this name
   * @param parameterTypes find method with most compatible parameters
   * @return The accessible method
   */
  public static Method getMatchingAccessibleMethod(
      final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
    try {
      final Method method = cls.getMethod(methodName, parameterTypes);
      MemberUtils.setAccessibleWorkaround(method);
      return method;
    } catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception
    }
    // search through all methods
    Method bestMatch = null;
    final Method[] methods = cls.getMethods();
    for (final Method method : methods) {
      // compare name and parameters
      if (method.getName().equals(methodName)
          && MemberUtils.isMatchingMethod(method, parameterTypes)) {
        // get accessible version of method
        final Method accessibleMethod = getAccessibleMethod(method);
        if (accessibleMethod != null
            && (bestMatch == null
                || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) {
          bestMatch = accessibleMethod;
        }
      }
    }
    if (bestMatch != null) {
      MemberUtils.setAccessibleWorkaround(bestMatch);
    }

    if (bestMatch != null
        && bestMatch.isVarArgs()
        && bestMatch.getParameterTypes().length > 0
        && parameterTypes.length > 0) {
      final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
      final Class<?> methodParameterComponentType =
          methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
      final String methodParameterComponentTypeName =
          ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
      final String parameterTypeName = parameterTypes[parameterTypes.length - 1].getName();
      final String parameterTypeSuperClassName =
          parameterTypes[parameterTypes.length - 1].getSuperclass().getName();

      if (!methodParameterComponentTypeName.equals(parameterTypeName)
          && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
        return null;
      }
    }

    return bestMatch;
  }
}


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

package play.http;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import javax.inject.Inject;
import javax.inject.Provider;

import com.typesafe.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Environment;
import play.api.OptionalSourceMapper;
import play.api.UsefulException;
import play.api.http.HttpErrorHandlerExceptions;
import play.api.routing.Router;
import play.mvc.Http.RequestHeader;
import play.mvc.Result;
import play.mvc.Results;
import scala.Option;
import scala.Some;

/**
 * Default implementation of the http error handler.
 *
 *
This class is intended to be extended to allow reusing Play's default error handling
 * functionality.
 *
 *
The "play.editor" configuration setting is used here to give a link back to the source code
 * when set and development mode is on.
 */
public class DefaultHttpErrorHandler implements HttpErrorHandler {

  private static final Logger logger = LoggerFactory.getLogger(DefaultHttpErrorHandler.class);

  private final Option<String> playEditor;
  private final Environment environment;
  private final OptionalSourceMapper sourceMapper;
  private final Provider<Router> routes;

  @Inject
  public DefaultHttpErrorHandler(
      Config config,
      Environment environment,
      OptionalSourceMapper sourceMapper,
      Provider<Router> routes) {
    this.environment = environment;
    this.sourceMapper = sourceMapper;
    this.routes = routes;

    this.playEditor =
        Option.apply(config.hasPath("play.editor") ? config.getString("play.editor") : null);
  }

  /**
   * Invoked when a client error occurs, that is, an error in the 4xx series.
   *
   *
The base implementation calls onBadRequest, onForbidden, onNotFound, or onOtherClientError
   * depending on the HTTP status code.
   *
   * @param request The request that caused the client error.
   * @param statusCode The error status code. Must be greater or equal to 400, and less than 500.
   * @param message The error message.
   * @return a CompletionStage containing the Result.
   */
  @Override
  public CompletionStage<Result> onClientError(
      RequestHeader request, int statusCode, String message) {
    if (statusCode == 400) {
      return onBadRequest(request, message);
    } else if (statusCode == 403) {
      return onForbidden(request, message);
    } else if (statusCode == 404) {
      return onNotFound(request, message);
    } else if (statusCode >= 400 && statusCode < 500) {
      return onOtherClientError(request, statusCode, message);
    } else {
      throw new IllegalArgumentException(
          "onClientError invoked with non client error status code " + statusCode + ": " + message);
    }
  }

  /**
   * Invoked when a client makes a bad request.
   *
   *
Returns Results.badRequest (400) with the included template from {@code
   * views.html.defaultpages.badRequest} as the content.
   *
   * @param request The request that was bad.
   * @param message The error message.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onBadRequest(RequestHeader request, String message) {
    return CompletableFuture.completedFuture(
        Results.badRequest(
            views.html.defaultpages.badRequest.render(
                request.method(), request.uri(), message, request.asScala())));
  }

  /**
   * Invoked when a client makes a request that was forbidden.
   *
   *
Returns Results.forbidden (401) with the included template from {@code
   * views.html.defaultpages.unauthorized} as the content.
   *
   * @param request The forbidden request.
   * @param message The error message.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onForbidden(RequestHeader request, String message) {
    return CompletableFuture.completedFuture(
        Results.forbidden(views.html.defaultpages.unauthorized.render(request.asScala())));
  }

  /**
   * Invoked when a handler or resource is not found.
   *
   *
If the environment's mode is production, then returns Results.notFound (404) with the
   * included template from `views.html.defaultpages.notFound` as the content.
   *
   *
Otherwise, Results.notFound (404) is rendered with {@code
   * views.html.defaultpages.devNotFound} template.
   *
   * @param request The request that no handler was found to handle.
   * @param message A message, which is not used by the default implementation.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onNotFound(RequestHeader request, String message) {
    if (environment.isProd()) {
      return CompletableFuture.completedFuture(
          Results.notFound(
              views.html.defaultpages.notFound.render(
                  request.method(), request.uri(), request.asScala())));
    } else {
      return CompletableFuture.completedFuture(
          Results.notFound(
              views.html.defaultpages.devNotFound.render(
                  request.method(), request.uri(), Some.apply(routes.get()), request.asScala())));
    }
  }

  /**
   * Invoked when a client error occurs, that is, an error in the 4xx series, which is not handled
   * by any of the other methods in this class already.
   *
   *
The base implementation uses {@code views.html.defaultpages.badRequest} template with the
   * given status.
   *
   * @param request The request that caused the client error.
   * @param statusCode The error status code. Must be greater or equal to 400, and less than 500.
   * @param message The error message.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onOtherClientError(
      RequestHeader request, int statusCode, String message) {
    return CompletableFuture.completedFuture(
        Results.status(
            statusCode,
            views.html.defaultpages.badRequest.render(
                request.method(), request.uri(), message, request.asScala())));
  }

  /**
   * Invoked when a server error occurs.
   *
   *
By default, the implementation of this method delegates to [[onProdServerError()]] when in
   * prod mode, and [[onDevServerError()]] in dev mode. It is recommended, if you want Play's debug
   * info on the error page in dev mode, that you override [[onProdServerError()]] instead of this
   * method.
   *
   * @param request The request that triggered the server error.
   * @param exception The server error.
   * @return a CompletionStage containing the Result.
   */
  @Override
  public CompletionStage<Result> onServerError(RequestHeader request, Throwable exception) {
    try {
      UsefulException usefulException = throwableToUsefulException(exception);

      logServerError(request, usefulException);

      switch (environment.mode()) {
        case PROD:
          return onProdServerError(request, usefulException);
        default:
          return onDevServerError(request, usefulException);
      }
    } catch (Exception e) {
      logger.error("Error while handling error", e);
      return CompletableFuture.completedFuture(Results.internalServerError());
    }
  }

  /**
   * Responsible for logging server errors.
   *
   *
The base implementation uses a SLF4J Logger. If a special annotation is desired for internal
   * server errors, you may want to use SLF4J directly with the Marker API to distinguish server
   * errors from application errors.
   *
   *
This can also be overridden to add additional logging information, eg. the id of the
   * authenticated user.
   *
   * @param request The request that triggered the server error.
   * @param usefulException The server error.
   */
  protected void logServerError(RequestHeader request, UsefulException usefulException) {
    logger.error(
        String.format(
            "\n\n! @%s - Internal server error, for (%s) [%s] ->\n",
            usefulException.id, request.method(), request.uri()),
        usefulException);
  }

  /**
   * Convert the given exception to an exception that Play can report more information about.
   *
   *
This will generate an id for the exception, and in dev mode, will load the source code for
   * the code that threw the exception, making it possible to report on the location that the
   * exception was thrown from.
   */
  protected final UsefulException throwableToUsefulException(final Throwable throwable) {
    return HttpErrorHandlerExceptions.throwableToUsefulException(
        sourceMapper.sourceMapper(), environment.isProd(), throwable);
  }

  /**
   * Invoked in dev mode when a server error occurs. Note that this method is where the URL set by
   * play.editor is used.
   *
   *
The base implementation returns {@code Results.internalServerError} with the content of
   * {@code views.html.defaultpages.devError}.
   *
   * @param request The request that triggered the error.
   * @param exception The exception.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onDevServerError(
      RequestHeader request, UsefulException exception) {
    return CompletableFuture.completedFuture(
        Results.internalServerError(
            views.html.defaultpages.devError.render(playEditor, exception, request.asScala())));
  }

  /**
   * Invoked in prod mode when a server error occurs.
   *
   *
The base implementation returns {@code Results.internalServerError} with the content of
   * {@code views.html.defaultpages.error} template.
   *
   *
Override this rather than [[onServerError()]] if you don't want to change Play's debug
   * output when logging errors in dev mode.
   *
   * @param request The request that triggered the error.
   * @param exception The exception.
   * @return a CompletionStage containing the Result.
   */
  protected CompletionStage<Result> onProdServerError(
      RequestHeader request, UsefulException exception) {
    return CompletableFuture.completedFuture(
        Results.internalServerError(
            views.html.defaultpages.error.render(exception, request.asScala())));
  }
}


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

package play.docs;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;

import play.core.BuildDocHandler;
import play.doc.FileRepository;
import play.doc.FilesystemRepository;
import play.doc.JarRepository;
import scala.Option;

/**
 * Provides a way for build code to create BuildDocHandler objects.
 *
 *
This class is used by the Play build plugin run command (to serve documentation from a JAR)
 * and by the Play documentation project (to serve documentation from the filesystem).
 *
 *
This class is written in Java and uses only Java types so that communication can work even
 * when the build code and the play-docs project are built with different versions of Scala.
 */
public class BuildDocHandlerFactory {

  /**
   * Create a BuildDocHandler that serves documentation from the given files, which could either be
   * directories or jar files. The baseDir array must be the same length as the files array, and the
   * corresponding entry in there for jar files is used as a base directory to use resources from in
   * the jar.
   *
   * @param files The directories or jar files to serve documentation from.
   * @param baseDirs The base directories for the jar files. Entries may be null.
   * @return a BuildDocHandler.
   */
  public static BuildDocHandler fromResources(File[] files, String[] baseDirs) throws IOException {
    assert (files.length == baseDirs.length);

    FileRepository[] repositories = new FileRepository[files.length];
    List<JarFile> jarFiles = new ArrayList<>();

    for (int i = 0; i < files.length; i++) {
      File file = files[i];
      String baseDir = baseDirs[i];

      if (file.isDirectory()) {
        repositories[i] = new FilesystemRepository(file);
      } else {
        // Assume it's a jar file
        JarFile jarFile = new JarFile(file);
        jarFiles.add(jarFile);
        repositories[i] = new JarRepository(jarFile, Option.apply(baseDir));
      }
    }

    return new DocumentationHandler(
        new AggregateFileRepository(repositories),
        () -> {
          for (JarFile jarFile : jarFiles) {
            jarFile.close();
          }
        });
  }

  /**
   * Create an BuildDocHandler that serves documentation from a given directory by wrapping a
   * FilesystemRepository.
   *
   * @param directory The directory to serve the documentation from.
   */
  public static BuildDocHandler fromDirectory(File directory) {
    FileRepository repo = new FilesystemRepository(directory);
    return new DocumentationHandler(repo);
  }

  /**
   * Create an BuildDocHandler that serves the manual from a given directory by wrapping a
   * FilesystemRepository, and the API docs from a given JAR file by wrapping a JarRepository
   *
   * @param directory The directory to serve the documentation from.
   * @param jarFile The JAR file to server the documentation from.
   * @param base The directory within the JAR file to serve the documentation from, or null if the
   *     documentation should be served from the root of the JAR.
   */
  public static BuildDocHandler fromDirectoryAndJar(File directory, JarFile jarFile, String base) {
    return fromDirectoryAndJar(directory, jarFile, base, false);
  }

  /**
   * Create an BuildDocHandler that serves the manual from a given directory by wrapping a
   * FilesystemRepository, and the API docs from a given JAR file by wrapping a JarRepository.
   *
   * @param directory The directory to serve the documentation from.
   * @param jarFile The JAR file to server the documentation from.
   * @param base The directory within the JAR file to serve the documentation from, or null if the
   *     documentation should be served from the root of the JAR.
   * @param fallbackToJar Whether the doc handler should fall back to the jar repo for docs.
   */
  public static BuildDocHandler fromDirectoryAndJar(
      File directory, JarFile jarFile, String base, boolean fallbackToJar) {
    FileRepository fileRepo = new FilesystemRepository(directory);
    FileRepository jarRepo = new JarRepository(jarFile, Option.apply(base));
    FileRepository manualRepo;
    if (fallbackToJar) {
      manualRepo = new AggregateFileRepository(new FileRepository[] {fileRepo, jarRepo});
    } else {
      manualRepo = fileRepo;
    }

    return new DocumentationHandler(manualRepo, jarRepo);
  }

  /**
   * Create an BuildDocHandler that serves documentation from a given JAR file by wrapping a
   * JarRepository.
   *
   * @param jarFile The JAR file to server the documentation from.
   * @param base The directory within the JAR file to serve the documentation from, or null if the
   *     documentation should be served from the root of the JAR.
   */
  public static BuildDocHandler fromJar(JarFile jarFile, String base) {
    FileRepository repo = new JarRepository(jarFile, Option.apply(base));
    return new DocumentationHandler(repo);
  }

  /**
   * Create a BuildDocHandler that doesn't do anything. Used when the documentation jar file is not
   * available.
   */
  public static BuildDocHandler empty() {
    return request -> Option.apply(null);
  }
}


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

package javaguide.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import play.libs.concurrent.HttpExecutionContext;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;

import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import static net.logstash.logback.marker.Markers.append;

public class JavaMarkerController extends Controller {
  private final HttpExecutionContext httpExecutionContext;

  @Inject
  public JavaMarkerController(HttpExecutionContext httpExecutionContext) {
    this.httpExecutionContext = httpExecutionContext;
  }

  private static final Logger logger = LoggerFactory.getLogger(JavaMarkerController.class);

  // #logging-log-info-with-request-context
  // ###insert: import static net.logstash.logback.marker.Markers.append;

  private Marker requestMarker(Http.Request request) {
    return append("host", request.host()).and(append("path", request.path()));
  }

  public Result index(Http.Request request) {
    logger.info(requestMarker(request), "Rendering index()");
    return ok("foo");
  }
  // #logging-log-info-with-request-context

  // #logging-log-info-with-async-request-context
  public CompletionStage<Result> asyncIndex(Http.Request request) {
    return CompletableFuture.supplyAsync(
        () -> {
          logger.info(requestMarker(request), "Rendering asyncIndex()");
          return ok("foo");
        },
        httpExecutionContext.current());
  }
  // #logging-log-info-with-async-request-context
}


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

package play.http;

import akka.japi.pf.PFBuilder;
import akka.stream.Materializer;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import play.api.http.HttpChunk;
import play.twirl.api.Content;
import play.twirl.api.Xml;
import scala.compat.java8.OptionConverters;

import java.util.Optional;
import java.util.concurrent.CompletionStage;

/** An HTTP entity */
public abstract class HttpEntity {

  // sealed
  private HttpEntity() {}

  /** @return The content type, if defined */
  public abstract Optional<String> contentType();

  /** @return Whether the entity is known to be empty or not. */
  public abstract boolean isKnownEmpty();

  /** @return The content length, if known */
  public abstract Optional<Long> contentLength();

  /** @return The stream of data. */
  public abstract Source<ByteString, ?> dataStream();

  /**
   * @param contentType the content type to use, i.e. "text/html".
   * @return Return the entity as the given content type.
   */
  public abstract HttpEntity as(String contentType);

  /**
   * Consumes the data.
   *
   * <p>This method should be used carefully, since if the source represents an ephemeral stream,
   * then the entity may not be usable after this method is invoked.
   *
   * @param mat the application's materializer.
   * @return a CompletionStage holding the data
   */
  public CompletionStage<ByteString> consumeData(Materializer mat) {
    return dataStream().runFold(ByteString.empty(), ByteString::concat, mat);
  }

  public abstract play.api.http.HttpEntity asScala();

  /** No entity. */
  public static final HttpEntity NO_ENTITY = new Strict(ByteString.empty(), Optional.empty());

  /**
   * Create an entity from the given content.
   *
   * @param content The content.
   * @param charset The charset.
   * @return the HTTP entity.
   */
  public static final HttpEntity fromContent(Content content, String charset) {
    String body;
    if (content instanceof Xml) {
      // See https://github.com/playframework/playframework/issues/2770
      body = content.body().trim();
    } else {
      body = content.body();
    }
    return new Strict(
        ByteString.fromString(body, charset),
        Optional.of(content.contentType() + "; charset=" + charset));
  }

  /**
   * Create an entity from the given String.
   *
   * @param content The content.
   * @param charset The charset.
   * @return the HTTP entity.
   */
  public static final HttpEntity fromString(String content, String charset) {
    return new Strict(
        ByteString.fromString(content, charset), Optional.of("text/plain; charset=" + charset));
  }

  /**
   * Convert the given source of ByteStrings to a chunked entity.
   *
   * @param data The source.
   * @param contentType The optional content type.
   * @return The ByteStrings.
   */
  public static final HttpEntity chunked(Source<ByteString, ?> data, Optional<String> contentType) {
    return new Chunked(data.map(HttpChunk.Chunk::new), contentType);
  }

  /** A strict entity, where all the data for it is in memory. */
  public static final class Strict extends HttpEntity {
    private final ByteString data;
    private final Optional<String> contentType;

    public Strict(ByteString data, Optional<String> contentType) {
      this.data = data;
      this.contentType = contentType;
    }

    public ByteString data() {
      return data;
    }

    @Override
    public Optional<String> contentType() {
      return contentType;
    }

    @Override
    public boolean isKnownEmpty() {
      return data.isEmpty();
    }

    @Override
    public Optional<Long> contentLength() {
      return Optional.of((long) data.length());
    }

    @Override
    public HttpEntity as(String contentType) {
      return new Strict(data, Optional.ofNullable(contentType));
    }

    @Override
    public Source<ByteString, ?> dataStream() {
      return Source.<ByteString>single(data);
    }

    @Override
    public play.api.http.HttpEntity asScala() {
      return new play.api.http.HttpEntity.Strict(data, OptionConverters.toScala(contentType));
    }
  }

  /** A streamed entity, backed by a source. */
  public static final class Streamed extends HttpEntity {
    private final Source<ByteString, ?> data;
    private final Optional<Long> contentLength;
    private final Optional<String> contentType;

    public Streamed(
        Source<ByteString, ?> data, Optional<Long> contentLength, Optional<String> contentType) {
      this.data = data;
      this.contentType = contentType;
      this.contentLength = contentLength;
    }

    public Source<ByteString, ?> data() {
      return data;
    }

    @Override
    public Optional<String> contentType() {
      return contentType;
    }

    @Override
    public boolean isKnownEmpty() {
      return false;
    }

    @Override
    public Optional<Long> contentLength() {
      return contentLength;
    }

    @Override
    public HttpEntity as(String contentType) {
      return new Streamed(data, contentLength, Optional.ofNullable(contentType));
    }

    @Override
    public Source<ByteString, ?> dataStream() {
      return data;
    }

    @Override
    @SuppressWarnings("unchecked")
    public play.api.http.HttpEntity asScala() {
      return new play.api.http.HttpEntity.Streamed(
          data.asScala(),
          /* scala Option[Long] produces a Java generic signature of Option<Object>, so we need to do an
          unchecked cast here to get it to typecheck */
          (scala.Option) OptionConverters.toScala(contentLength),
          OptionConverters.toScala(contentType));
    }
  }

  /** A chunked entity, backed by a source of chunks. */
  public static final class Chunked extends HttpEntity {
    private final Source<HttpChunk, ?> chunks;
    private final Optional<String> contentType;

    public Chunked(Source<HttpChunk, ?> chunks, Optional<String> contentType) {
      this.chunks = chunks;
      this.contentType = contentType;
    }

    public Source<HttpChunk, ?> chunks() {
      return chunks;
    }

    @Override
    public Optional<String> contentType() {
      return contentType;
    }

    @Override
    public boolean isKnownEmpty() {
      return false;
    }

    @Override
    public Optional<Long> contentLength() {
      return Optional.empty();
    }

    @Override
    public HttpEntity as(String contentType) {
      return new Chunked(chunks, Optional.ofNullable(contentType));
    }

    @Override
    public Source<ByteString, ?> dataStream() {
      return chunks.<ByteString>collect(
          new PFBuilder<HttpChunk, ByteString>()
              .match(HttpChunk.Chunk.class, HttpChunk.Chunk::data)
              .build());
    }

    @Override
    public play.api.http.HttpEntity asScala() {
      return new play.api.http.HttpEntity.Chunked(
          chunks.asScala(), OptionConverters.toScala(contentType));
    }
  }
}


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

package javaguide.binder.models;

import java.util.Map;
import java.util.Optional;
import play.libs.F;
import play.libs.F.*;
import play.mvc.QueryStringBindable;
// import java.util.Optional;

// #declaration
public class AgeRange implements QueryStringBindable<AgeRange> {

  public Integer from;
  public Integer to;
  // #declaration

  // #bind
  @Override
  public Optional<AgeRange> bind(String key, Map<String, String[]> data) {

    try {
      from = Integer.valueOf(data.get("from")[0]);
      to = Integer.valueOf(data.get("to")[0]);
      return Optional.of(this);

    } catch (Exception e) { // no parameter match return None
      return Optional.empty();
    }
  }

  @Override
  public String unbind(String key) {
    return new StringBuilder().append("from=").append(from).append("&to=").append(to).toString();
  }
  // #bind

  @Override
  public String javascriptUnbind() {
    return new StringBuilder().append("from=").append(from).append(";to=").append(to).toString();
  }
}