text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>Uses of Package org.springframework.core.io (Spring Framework 5.1.18.RELEASE API)</title>
<meta name="date" content="2020-09-15">
<link rel="stylesheet" type="text/css" href="../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Spring Framework

Prev

Next

Frames

No Frames

All Classes

Uses of Package
org.springframework.core.io

Packages that use org.springframework.core.io

Package
Description

org.springframework.beans.factory.config

SPI interfaces and configuration-related convenience classes for bean factories.

org.springframework.beans.factory.groovy

Support package for Groovy-based bean definitions.

org.springframework.beans.factory.parsing

Support infrastructure for bean definition parsing.

org.springframework.beans.factory.support

Classes supporting the org.springframework.beans.factory package.

org.springframework.beans.factory.xml

Contains an abstract XML-based BeanFactory implementation,
 including a standard "spring-beans" XSD.

org.springframework.beans.propertyeditors

Properties editors used to convert from String values to object
 types such as java.util.Properties.

org.springframework.beans.support

Classes supporting the org.springframework.beans package,
 such as utility classes for sorting and holding lists of beans.

org.springframework.cache.ehcache

Support classes for the open source cache
 EhCache 2.x,
 allowing to set up an EhCache CacheManager and Caches
 as beans in a Spring context.

org.springframework.context

This package builds on the beans package to add support for
 message sources and for the Observer design pattern, and the
 ability for application objects to obtain resources using a
 consistent API.

org.springframework.context.annotation

Annotation support for the Application Context, including JSR-250 "common"
 annotations, component-scanning, and Java-based metadata for creating
 Spring-managed objects.

org.springframework.context.support

Classes supporting the org.springframework.context package,
 such as abstract base classes for ApplicationContext
 implementations and a MessageSource implementation.

org.springframework.core.codec

Encoder and
 Decoder abstractions to convert
 between a reactive stream of bytes and Java objects.

org.springframework.core.io

Generic abstraction for (file-based) resources, used throughout the framework.

org.springframework.core.io.buffer

Generic abstraction for working with byte buffer implementations.

org.springframework.core.io.support

Support classes for Spring's resource abstraction.

org.springframework.core.type.classreading

Support classes for reading annotation and class-level metadata.

org.springframework.http

Contains a basic abstraction over client/server-side HTTP.

org.springframework.http.codec

Provides implementations of Encoder
 and Decoder for web use.

org.springframework.http.converter

Provides an HttpMessageConverter abstraction to convert between Java objects and HTTP input/output messages.

org.springframework.jca.context

Integration package that allows for deploying a Spring application context
 as a JCA 1.7 compliant RAR file.

org.springframework.jdbc.config

Defines the Spring JDBC configuration namespace.

org.springframework.jdbc.datasource.embedded

Provides extensible support for creating embedded database instances.

org.springframework.jdbc.datasource.init

Provides extensible support for initializing databases through scripts.

org.springframework.jdbc.support

Support classes for the JDBC framework, used by the classes in the
 jdbc.core and jdbc.object packages.

org.springframework.jmx.export.naming

Provides a strategy for ObjectName creation.

org.springframework.mail.javamail

JavaMail support for Spring's mail infrastructure.

org.springframework.mock.web

A comprehensive set of Servlet API 3.0 mock objects,
 targeted at usage with Spring's Web MVC framework.

org.springframework.orm.hibernate5

Package providing integration of
 Hibernate 5.x
 with Spring concepts.

org.springframework.orm.jpa

Package providing integration of JPA (Java Persistence API) with Spring concepts.

org.springframework.orm.jpa.persistenceunit

Internal support for managing JPA persistence units.

org.springframework.oxm.castor

Package providing integration of Castor
 within Spring's O/X Mapping support.

org.springframework.oxm.jaxb

Package providing integration of JAXB
 with Spring's O/X Mapping support.

org.springframework.oxm.support

Provides generic support classes for using Spring's O/X Mapping integration
 within various scenario's.

org.springframework.remoting.jaxws

Remoting classes for Web Services via JAX-WS (the successor of JAX-RPC),
 as included in Java 6 and Java EE 5.

org.springframework.scheduling.quartz

Support classes for the open source scheduler
 Quartz,
 allowing to set up Quartz Schedulers, JobDetails and
 Triggers as beans in a Spring context.

org.springframework.scripting.support

Support classes for Spring's scripting package.

org.springframework.test.context.support

Support classes for the Spring TestContext Framework.

org.springframework.test.context.util

Common utilities used within the Spring TestContext Framework.

org.springframework.test.web.client.response

Contains built-in ResponseCreator
 implementations.

org.springframework.ui.freemarker

Support classes for setting up
 FreeMarker
 within a Spring application context.

org.springframework.validation.beanvalidation

Support classes for integrating a JSR-303 Bean Validation provider
 (such as Hibernate Validator) into a Spring ApplicationContext
 and in particular with Spring's data binding and validation APIs.

org.springframework.web.accept

This package contains classes used to determine the requested the media types in a request.

org.springframework.web.context

Contains a variant of the application context interface for web applications,
 and the ContextLoaderListener that bootstraps a root web application context.

org.springframework.web.context.support

Classes supporting the org.springframework.web.context package,
 such as WebApplicationContext implementations and various utility classes.

org.springframework.web.multipart

Multipart resolution framework for handling file uploads.

org.springframework.web.multipart.commons

MultipartResolver implementation for
 Apache Commons FileUpload.

org.springframework.web.reactive.config

Spring WebFlux configuration infrastructure.

org.springframework.web.reactive.function

Provides a foundation for both the reactive client and server subpackages.

org.springframework.web.reactive.function.server

Provides the types that make up Spring's functional web framework.

org.springframework.web.reactive.resource

Support classes for serving static resources.

org.springframework.web.reactive.result.view.script

Support classes for views based on the JSR-223 script engine abstraction
 (as included in Java 6+), e.g.

org.springframework.web.servlet.resource

Support classes for serving static resources.

org.springframework.web.servlet.view

Provides standard View and ViewResolver implementations,
 including abstract base classes for custom implementations.

Classes in org.springframework.core.io used by org.springframework.beans.factory.config

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.beans.factory.groovy

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.beans.factory.parsing

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.beans.factory.support

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.beans.factory.xml

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.beans.propertyeditors

Class and Description

ResourceEditor

Editor for Resource
 descriptors, to automatically convert String locations
 e.g.

Classes in org.springframework.core.io used by org.springframework.beans.support

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.cache.ehcache

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.context

Class and Description

ProtocolResolver

A resolution strategy for protocol-specific resource handles.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.context.annotation

Class and Description

DefaultResourceLoader

Default implementation of the ResourceLoader interface.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.context.support

Class and Description

DefaultResourceLoader

Default implementation of the ResourceLoader interface.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.core.codec

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.core.io

Class and Description

AbstractFileResolvingResource

Abstract base class for resources which resolve URLs into File references,
 such as UrlResource or ClassPathResource.

AbstractResource

Convenience base class for Resource implementations,
 pre-implementing typical behavior.

ClassPathResource

Resource implementation for class path resources.

ContextResource

Extended interface for a resource that is loaded from an enclosing
 'context', e.g.

DefaultResourceLoader

Default implementation of the ResourceLoader interface.

InputStreamSource

Simple interface for objects that are sources for an InputStream.

ProtocolResolver

A resolution strategy for protocol-specific resource handles.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

UrlResource

Resource implementation for java.net.URL locators.

WritableResource

Extended interface for a resource that supports writing to it.

Classes in org.springframework.core.io used by org.springframework.core.io.buffer

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.core.io.support

Class and Description

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.core.type.classreading

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.http

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.http.codec

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.http.converter

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.jca.context

Class and Description

DefaultResourceLoader

Default implementation of the ResourceLoader interface.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.jdbc.config

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.jdbc.datasource.embedded

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.jdbc.datasource.init

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.jdbc.support

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.jmx.export.naming

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.mail.javamail

Class and Description

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.mock.web

Class and Description

InputStreamSource

Simple interface for objects that are sources for an InputStream.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.orm.hibernate5

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.orm.jpa

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.orm.jpa.persistenceunit

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.oxm.castor

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.oxm.jaxb

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.oxm.support

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.remoting.jaxws

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.scheduling.quartz

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.scripting.support

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.test.context.support

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.test.context.util

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.test.web.client.response

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.ui.freemarker

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.validation.beanvalidation

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.accept

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.context

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.web.context.support

Class and Description

AbstractFileResolvingResource

Abstract base class for resources which resolve URLs into File references,
 such as UrlResource or ClassPathResource.

AbstractResource

Convenience base class for Resource implementations,
 pre-implementing typical behavior.

ContextResource

Extended interface for a resource that is loaded from an enclosing
 'context', e.g.

DefaultResourceLoader

Default implementation of the ResourceLoader interface.

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.web.multipart

Class and Description

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.multipart.commons

Class and Description

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.reactive.config

Class and Description

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.web.reactive.function

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.reactive.function.server

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.reactive.resource

Class and Description

AbstractResource

Convenience base class for Resource implementations,
 pre-implementing typical behavior.

ByteArrayResource

Resource implementation for a given byte array.

InputStreamSource

Simple interface for objects that are sources for an InputStream.

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

ResourceLoader

Strategy interface for loading resources (e..

Classes in org.springframework.core.io used by org.springframework.web.reactive.result.view.script

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.servlet.resource

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Classes in org.springframework.core.io used by org.springframework.web.servlet.view

Class and Description

Resource

Interface for a resource descriptor that abstracts from the actual
 type of underlying resource, such as a file or class path resource.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Spring Framework

Prev

Next

Frames

No Frames

All Classes


text:
Using Scene Lab    {#scene_lab_using}
===============

# User Interface

## Activating Scene Lab

It's up to your game to manage when to activate Scene Lab. Maybe you'll set this
to a keypress, or an option in your pause menu. Scene Lab is designed to be
activated and deactivated repeatedly within a level to make changes, test them,
tweak them, etc.

## The Menu Bar

The top of the screen contains the menu bar, more correctly called a button
bar. This contains a context-sensitive collection of buttons, including:

Menu Button | Effect
--- | ---
Save Scene | Save all entities to disk.
Exit Scene Lab | Exit back to your game. Prompts the user to save if they haven't already.

# Moving Around

Scene Lab has two overall modes: Movement mode and Edit mode. In Movement mode,
you can move around your game's environment by using the WASD keys (and, if the
ground-parallel movement config option is enabled, with the R and F keys to move
up and down) and moving the mouse.

You can toggle to and from Edit mode by clicking the right mouse button.

Key | Movement Mode Action
--- | ---
W | Move the camera forwards.
A | Move the camera to the left.
S | Move the camera backwards.
D | Move the camera to the right.
R | If ground-parallel movement is enabled, move the camera up.
F | If ground-parallel movement is enabled, move the camera down.
Right-click mouse | Enter Edit mode.

# Selecting and Editing Entities

In Edit Mode, your mouse cursor will be visible, and you can select entities by
clicking on them. You can then interact further with the selected entity,
whether editing its properties or using the mouse to move it around the scene.

## Selecting Entities

In Edit mode, clicking on an object with the mouse will select it. In technical
terms, it will cast a ray from the screen in the direction you clicked, and
determine which entity the ray intersects based on its PhysicsComponent bounding
box. These bounding boxes are normally automatically generated for entities with
a RenderMesh, but if your object has its own PhysicsComponent data and physics
shapes, it will use those instead.

Note that because the bounding boxes are larger than the objects they contain,
it is possible to select an object even though it seems like you are clicking on
the object behind it. You can always turn on Show Physics in the Settings tab to
look at the physics shapes for the selected entity.

If you want to go back to Movement mode, click the right mouse button, which
toggles between the two modes.

## Moving Entities Around

After you have selected an entity, click and hold the mouse button down on that
entity to move it around the scene, or rotate or scale it. The action performed
depends on which Mouse Tool you have selected.

### Mouse Tools

On the lower-left of the screen is the mouse tool selector. Clicking on this
lets you toggle the mouse tool used when editing an object. The choices are:

Mouse Tool | Effect when dragging the selected entity around.
--- | ---
Move Horizontal | Move the object along the ground plane,
Move Vertical | Move the object up and down along its current X/Y position, or left/right along its plane parallel to the clipping plane.
Rotate Horizontal | Change the object's rotation about the axis perpendicular to the ground.
Rotate Vertical | Change the object's rotation about the axis consisting of the camera's forward direction projected parallel to the ground plane.
Scale All | Change the object's overall scale.
Scale X | Change the object's X scale.
Scale Y | Change the object's Y scale.
Scale Z | Change the object's Z scale.

## Editing via the Keyboard

When you have an entity selected, you can use predefined keyboard keys to adjust
certain aspects of the entity's transform. These work whether you are in
Movement or Edit mode.

### Translating

Key | Action
--- | ---
I | Move entity forward (in the positive-X direction).
J | Move entity backwards (in the negative-X direction).
K | Move entity to the left (in the negative-Y direction).
L | Move entity to the right (in the positive-Y direction).
P | Move entity up (in the positive-Z direction).
; (semicolon) | Move entity down (in the negative-Z direction).
SHIFT | Hold SHIFT to move the entity more slowly.

### Rotating

Key | Action
--- | ---
U | Rotate the entity clockwise about the X axis.
O | Rotate the entity counter-clockwise about the X axis.
Y | Rotate the entity clockwise about the Y axis.
H | Rotate the entity counter-clockwise about the Y axis.
N | Rotate the entity clockwise about the Z axis.
M | Rotate the entity counter-clockwise about the Z axis.
SHIFT | Hold SHIFT to rotate the entity more slowly.

### Scaling

Key | Action
--- | ---
+ (plus) or = (equals) | Scale the entity to be larger in all 3 axes.
- (minus) | Scale the entity to be smaller in all 3 axes.
LCTRL and 0 (zero) | Set the entity's scale to (1, 1, 1).

### Miscellaneous Keyboard Shortcuts

Key | Action
--- | ---
INSERT or V key | Make a copy of the entity and select the new copy so you can edit it.
DELETE or X key | Delete the currently selected entity from the world.
[ (left bracket) | Select the next entity in the entity list.
] (right bracket) | Select the previous entity in the entity list.

# The Edit Window

Depending on what you are doing, there are several edit windows that look
similar but operate somewhat differently.

The edit window may contain more data than can fit on the screen. If it does,
you can scroll the window around by clicking and dragging the mouse pointer on
the window, or by using your mouse wheel.

### Edit Entity

Edit component properties for the currently-selected entity.
See scene_lab::FlatbufferEditor for more information.

### List Entities

List all entities in the scene, by their entity ID and prototype.

You can do a text search for a specific entity
or prototype name. Clicking on an entity button once selects it, and clicking
again shows you the properties for that entity, allowing you to edit them.

### Settings
Change some settings in Scene Lab.

Setting | Effect
--- | ---
Data types: On / Off | Show data type names for subtables, structs, and enums. Click to toggle.
Show physics: On / Off | Show the physics shapes for the selected entity. Click to toggle.
Expand all: On / Off | Expand all subtables in the Flatbuffer view; otherwise you must click on the "..." next to a table to expand it. Click to toggle.
Ground-Parallel Camera: On / Off | If On, all W-A-S-D keyboard movement will be parallel to the ground, and you must use the R and F keys to move up and down. If Off, movement will be in the direction of the camera.
Maximize View | Click to maximize the edit window to fill the entire screen.
Hide View | Click to hide the edit window. Click any tab to show the edit window again.

redpajama_set_name: RedPajamaGithub


text:
package io.druid.query.groupby.epinephelinae;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.ListeningExecutorService;
import io.druid.collections.ResourceHolder;
import io.druid.common.utils.IntArrayUtils;
import io.druid.data.input.MapBasedRow;
import io.druid.data.input.Row;
import io.druid.java.util.common.IAE;
import io.druid.java.util.common.ISE;
import io.druid.java.util.common.Pair;
import io.druid.java.util.common.granularity.AllGranularity;
import io.druid.java.util.common.guava.Accumulator;
import io.druid.query.BaseQuery;
import io.druid.query.ColumnSelectorPlus;
import io.druid.query.aggregation.AggregatorFactory;
import io.druid.query.dimension.ColumnSelectorStrategy;
import io.druid.query.dimension.ColumnSelectorStrategyFactory;
import io.druid.query.dimension.DimensionSpec;
import io.druid.query.groupby.GroupByQuery;
import io.druid.query.groupby.GroupByQueryConfig;
import io.druid.query.groupby.RowBasedColumnSelectorFactory;
import io.druid.query.groupby.epinephelinae.Grouper.BufferComparator;
import io.druid.query.groupby.orderby.DefaultLimitSpec;
import io.druid.query.groupby.orderby.OrderByColumnSpec;
import io.druid.query.groupby.strategy.GroupByStrategyV2;
import io.druid.query.ordering.StringComparator;
import io.druid.query.ordering.StringComparators;
import io.druid.segment.BaseDoubleColumnValueSelector;
import io.druid.segment.BaseFloatColumnValueSelector;
import io.druid.segment.BaseLongColumnValueSelector;
import io.druid.segment.ColumnSelectorFactory;
import io.druid.segment.ColumnValueSelector;
import io.druid.segment.DimensionHandlerUtils;
import io.druid.segment.DimensionSelector;
import io.druid.segment.column.ColumnCapabilities;
import io.druid.segment.column.ValueType;
import io.druid.segment.data.IndexedInts;
import it.unimi.dsi.fastutil.ints.IntArrays;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.joda.time.DateTime;

import javax.annotation.Nullable;
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.IntStream;

// this class contains shared code between GroupByMergingQueryRunnerV2 and GroupByRowProcessor
public class RowBasedGrouperHelper
{
  // Entry in dictionary, node pointer in reverseDictionary, hash + k/v/next pointer in reverseDictionary nodes
  private static final int ROUGH_OVERHEAD_PER_DICTIONARY_ENTRY = Long.BYTES * 5 + Integer.BYTES;

  private static final int SINGLE_THREAD_CONCURRENCY_HINT = -1;
  private static final int UNKNOWN_THREAD_PRIORITY = -1;
  private static final long UNKNOWN_TIMEOUT = -1L;

  /**
   * Create a single-threaded grouper and accumulator.
   */
  public static Pair<Grouper<RowBasedKey>, Accumulator<AggregateResult, Row>> createGrouperAccumulatorPair(
      final GroupByQuery query,
      final boolean isInputRaw,
      final Map<String, ValueType> rawInputRowSignature,
      final GroupByQueryConfig config,
      final Supplier<ByteBuffer> bufferSupplier,
      final LimitedTemporaryStorage temporaryStorage,
      final ObjectMapper spillMapper,
      final AggregatorFactory[] aggregatorFactories,
      final int mergeBufferSize
  )
  {
    return createGrouperAccumulatorPair(
        query,
        isInputRaw,
        rawInputRowSignature,
        config,
        bufferSupplier,
        null,
        SINGLE_THREAD_CONCURRENCY_HINT,
        temporaryStorage,
        spillMapper,
        aggregatorFactories,
        null,
        UNKNOWN_THREAD_PRIORITY,
        false,
        UNKNOWN_TIMEOUT,
        mergeBufferSize
    );
  }

  /**
   * If isInputRaw is true, transformations such as timestamp truncation and extraction functions have not
   * been applied to the input rows yet, for example, in a nested query, if an extraction function is being
   * applied in the outer query to a field of the inner query. This method must apply those transformations.
   */
  public static Pair<Grouper<RowBasedKey>, Accumulator<AggregateResult, Row>> createGrouperAccumulatorPair(
      final GroupByQuery query,
      final boolean isInputRaw,
      final Map<String, ValueType> rawInputRowSignature,
      final GroupByQueryConfig config,
      final Supplier<ByteBuffer> bufferSupplier,
      final Supplier<ResourceHolder<ByteBuffer>> combineBufferSupplier,
      final int concurrencyHint,
      final LimitedTemporaryStorage temporaryStorage,
      final ObjectMapper spillMapper,
      final AggregatorFactory[] aggregatorFactories,
      @Nullable final ListeningExecutorService grouperSorter,
      final int priority,
      final boolean hasQueryTimeout,
      final long queryTimeoutAt,
      final int mergeBufferSize
  )
  {
    // concurrencyHint >= 1 for concurrent groupers, -1 for single-threaded
    Preconditions.checkArgument(concurrencyHint >= 1 || concurrencyHint == -1, "invalid concurrencyHint");

    final List<ValueType> valueTypes = DimensionHandlerUtils.getValueTypesFromDimensionSpecs(query.getDimensions());

    final GroupByQueryConfig querySpecificConfig = config.withOverrides(query);
    final boolean includeTimestamp = GroupByStrategyV2.getUniversalTimestamp(query) == null;

    final ThreadLocal<Row> columnSelectorRow = new ThreadLocal<>();
    final ColumnSelectorFactory columnSelectorFactory = query.getVirtualColumns().wrap(
        RowBasedColumnSelectorFactory.create(
            columnSelectorRow,
            rawInputRowSignature
        )
    );

    final boolean willApplyLimitPushDown = query.isApplyLimitPushDown();
    final DefaultLimitSpec limitSpec = willApplyLimitPushDown ? (DefaultLimitSpec) query.getLimitSpec() : null;
    boolean sortHasNonGroupingFields = false;
    if (willApplyLimitPushDown) {
      sortHasNonGroupingFields = DefaultLimitSpec.sortingOrderHasNonGroupingFields(
          limitSpec,
          query.getDimensions()
      );
    }

    final Grouper.KeySerdeFactory<RowBasedKey> keySerdeFactory = new RowBasedKeySerdeFactory(
        includeTimestamp,
        query.getContextSortByDimsFirst(),
        query.getDimensions(),
        querySpecificConfig.getMaxMergingDictionarySize() / (concurrencyHint == -1 ? 1 : concurrencyHint),
        valueTypes,
        aggregatorFactories,
        limitSpec
    );

    final Grouper<RowBasedKey> grouper;
    if (concurrencyHint == -1) {
      grouper = new SpillingGrouper<>(
          bufferSupplier,
          keySerdeFactory,
          columnSelectorFactory,
          aggregatorFactories,
          querySpecificConfig.getBufferGrouperMaxSize(),
          querySpecificConfig.getBufferGrouperMaxLoadFactor(),
          querySpecificConfig.getBufferGrouperInitialBuckets(),
          temporaryStorage,
          spillMapper,
          true,
          limitSpec,
          sortHasNonGroupingFields,
          mergeBufferSize
      );
    } else {
      final Grouper.KeySerdeFactory<RowBasedKey> combineKeySerdeFactory = new RowBasedKeySerdeFactory(
          includeTimestamp,
          query.getContextSortByDimsFirst(),
          query.getDimensions(),
          querySpecificConfig.getMaxMergingDictionarySize(), // use entire dictionary space for combining key serde
          valueTypes,
          aggregatorFactories,
          limitSpec
      );

      grouper = new ConcurrentGrouper<>(
          querySpecificConfig,
          bufferSupplier,
          combineBufferSupplier,
          keySerdeFactory,
          combineKeySerdeFactory,
          columnSelectorFactory,
          aggregatorFactories,
          temporaryStorage,
          spillMapper,
          concurrencyHint,
          limitSpec,
          sortHasNonGroupingFields,
          grouperSorter,
          priority,
          hasQueryTimeout,
          queryTimeoutAt
      );
    }

    final int keySize = includeTimestamp ? query.getDimensions().size() + 1 : query.getDimensions().size();
    final ValueExtractFunction valueExtractFn = makeValueExtractFunction(
        query,
        isInputRaw,
        includeTimestamp,
        columnSelectorFactory,
        valueTypes
    );

    final Accumulator<AggregateResult, Row> accumulator = new Accumulator<AggregateResult, Row>()
    {
      @Override
      public AggregateResult accumulate(
          final AggregateResult priorResult,
          final Row row
      )
      {
        BaseQuery.checkInterrupted();

        if (priorResult != null && !priorResult.isOk()) {
          // Pass-through error returns without doing more work.
          return priorResult;
        }

        if (!grouper.isInitialized()) {
          grouper.init();
        }

        columnSelectorRow.set(row);

        final Comparable[] key = new Comparable[keySize];
        valueExtractFn.apply(row, key);

        final AggregateResult aggregateResult = grouper.aggregate(new RowBasedKey(key));
        columnSelectorRow.set(null);

        return aggregateResult;
      }
    };

    return new Pair<>(grouper, accumulator);
  }

  private interface TimestampExtractFunction
  {
    long apply(Row row);
  }

  private static TimestampExtractFunction makeTimestampExtractFunction(
      final GroupByQuery query,
      final boolean isInputRaw
  )
  {
    if (isInputRaw) {
      if (query.getGranularity() instanceof AllGranularity) {
        return new TimestampExtractFunction()
        {
          @Override
          public long apply(Row row)
          {
            return query.getIntervals().get(0).getStartMillis();
          }
        };
      } else {
        return new TimestampExtractFunction()
        {
          @Override
          public long apply(Row row)
          {
            return query.getGranularity().bucketStart(row.getTimestamp()).getMillis();
          }
        };
      }
    } else {
      return new TimestampExtractFunction()
      {
        @Override
        public long apply(Row row)
        {
          return row.getTimestampFromEpoch();
        }
      };
    }
  }

  private interface ValueExtractFunction
  {
    Comparable[] apply(Row row, Comparable[] key);
  }

  private static ValueExtractFunction makeValueExtractFunction(
      final GroupByQuery query,
      final boolean isInputRaw,
      final boolean includeTimestamp,
      final ColumnSelectorFactory columnSelectorFactory,
      final List<ValueType> valueTypes
  )
  {
    final TimestampExtractFunction timestampExtractFn = includeTimestamp ?
                                                        makeTimestampExtractFunction(query, isInputRaw) :
                                                        null;

    final Function<Comparable, Comparable>[] valueConvertFns = makeValueConvertFunctions(valueTypes);

    if (isInputRaw) {
      final Supplier<Comparable>[] inputRawSuppliers = getValueSuppliersForDimensions(
          columnSelectorFactory,
          query.getDimensions()
      );

      if (includeTimestamp) {
        return new ValueExtractFunction()
        {
          @Override
          public Comparable[] apply(Row row, Comparable[] key)
          {
            key[0] = timestampExtractFn.apply(row);
            for (int i = 1; i < key.length; i++) {
              final Comparable val = inputRawSuppliers[i - 1].get();
              key[i] = valueConvertFns[i - 1].apply(val);
            }
            return key;
          }
        };
      } else {
        return new ValueExtractFunction()
        {
          @Override
          public Comparable[] apply(Row row, Comparable[] key)
          {
            for (int i = 0; i < key.length; i++) {
              final Comparable val = inputRawSuppliers[i].get();
              key[i] = valueConvertFns[i].apply(val);
            }
            return key;
          }
        };
      }
    } else {
      if (includeTimestamp) {
        return new ValueExtractFunction()
        {
          @Override
          public Comparable[] apply(Row row, Comparable[] key)
          {
            key[0] = timestampExtractFn.apply(row);
            for (int i = 1; i < key.length; i++) {
              final Comparable val = (Comparable) row.getRaw(query.getDimensions().get(i - 1).getOutputName());
              key[i] = valueConvertFns[i - 1].apply(val);
            }
            return key;
          }
        };
      } else {
        return new ValueExtractFunction()
        {
          @Override
          public Comparable[] apply(Row row, Comparable[] key)
          {
            for (int i = 0; i < key.length; i++) {
              final Comparable val = (Comparable) row.getRaw(query.getDimensions().get(i).getOutputName());
              key[i] = valueConvertFns[i].apply(val);
            }
            return key;
          }
        };
      }
    }
  }

  public static CloseableGrouperIterator<RowBasedKey, Row> makeGrouperIterator(
      final Grouper<RowBasedKey> grouper,
      final GroupByQuery query,
      final Closeable closeable
  )
  {
    final boolean includeTimestamp = GroupByStrategyV2.getUniversalTimestamp(query) == null;

    return new CloseableGrouperIterator<>(
        grouper,
        true,
        new Function<Grouper.Entry<RowBasedKey>, Row>()
        {
          @Override
          public Row apply(Grouper.Entry<RowBasedKey> entry)
          {
            Map<String, Object> theMap = Maps.newLinkedHashMap();

            // Get timestamp, maybe.
            final DateTime timestamp;
            final int dimStart;

            if (includeTimestamp) {
              timestamp = query.getGranularity().toDateTime(((long) (entry.getKey().getKey()[0])));
              dimStart = 1;
            } else {
              timestamp = null;
              dimStart = 0;
            }

            // Add dimensions.
            for (int i = dimStart; i < entry.getKey().getKey().length; i++) {
              Object dimVal = entry.getKey().getKey()[i];
              theMap.put(
                  query.getDimensions().get(i - dimStart).getOutputName(),
                  dimVal instanceof String ? Strings.emptyToNull((String) dimVal) : dimVal
              );
            }

            // Add aggregations.
            for (int i = 0; i < entry.getValues().length; i++) {
              theMap.put(query.getAggregatorSpecs().get(i).getName(), entry.getValues()[i]);
            }

            return new MapBasedRow(timestamp, theMap);
          }
        },
        closeable
    );
  }

  static class RowBasedKey
  {
    private final Object[] key;

    RowBasedKey(final Object[] key)
    {
      this.key = key;
    }

    @JsonCreator
    public static RowBasedKey fromJsonArray(final Object[] key)
    {
      // Type info is lost during serde:
      // Floats may be deserialized as doubles, Longs may be deserialized as integers, convert them back
      for (int i = 0; i < key.length; i++) {
        if (key[i] instanceof Integer) {
          key[i] = ((Integer) key[i]).longValue();
        } else if (key[i] instanceof Double) {
          key[i] = ((Double) key[i]).floatValue();
        }
      }

      return new RowBasedKey(key);
    }

    @JsonValue
    public Object[] getKey()
    {
      return key;
    }

    @Override
    public boolean equals(Object o)
    {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }

      RowBasedKey that = (RowBasedKey) o;

      return Arrays.equals(key, that.key);
    }

    @Override
    public int hashCode()
    {
      return Arrays.hashCode(key);
    }

    @Override
    public String toString()
    {
      return Arrays.toString(key);
    }
  }

  private static final InputRawSupplierColumnSelectorStrategyFactory STRATEGY_FACTORY =
      new InputRawSupplierColumnSelectorStrategyFactory();

  private interface InputRawSupplierColumnSelectorStrategy<ValueSelectorType> extends ColumnSelectorStrategy
  {
    Supplier<Comparable> makeInputRawSupplier(ValueSelectorType selector);
  }

  private static class StringInputRawSupplierColumnSelectorStrategy
      implements InputRawSupplierColumnSelectorStrategy<DimensionSelector>
  {
    @Override
    public Supplier<Comparable> makeInputRawSupplier(DimensionSelector selector)
    {
      return new Supplier<Comparable>()
      {
        @Override
        public Comparable get()
        {
          final String value;
          IndexedInts index = selector.getRow();
          value = index.size() == 0
                  ? ""
                  : selector.lookupName(index.get(0));
          return Strings.nullToEmpty(value);
        }
      };
    }
  }

  private static class InputRawSupplierColumnSelectorStrategyFactory
      implements ColumnSelectorStrategyFactory<InputRawSupplierColumnSelectorStrategy>
  {
    @Override
    public InputRawSupplierColumnSelectorStrategy makeColumnSelectorStrategy(
        ColumnCapabilities capabilities, ColumnValueSelector selector
    )
    {
      ValueType type = capabilities.getType();
      switch (type) {
        case STRING:
          return new StringInputRawSupplierColumnSelectorStrategy();
        case LONG:
          return (InputRawSupplierColumnSelectorStrategy<BaseLongColumnValueSelector>)
              columnSelector -> columnSelector::getLong;
        case FLOAT:
          return (InputRawSupplierColumnSelectorStrategy<BaseFloatColumnValueSelector>)
              columnSelector -> columnSelector::getFloat;
        case DOUBLE:
          return (InputRawSupplierColumnSelectorStrategy<BaseDoubleColumnValueSelector>)
              columnSelector -> columnSelector::getDouble;
        default:
          throw new IAE("Cannot create query type helper from invalid type [%s]", type);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private static Supplier<Comparable>[] getValueSuppliersForDimensions(
      final ColumnSelectorFactory columnSelectorFactory,
      final List<DimensionSpec> dimensions
  )
  {
    final Supplier[] inputRawSuppliers = new Supplier[dimensions.size()];
    final ColumnSelectorPlus[] selectorPluses = DimensionHandlerUtils.createColumnSelectorPluses(
        STRATEGY_FACTORY,
        dimensions,
        columnSelectorFactory
    );

    for (int i = 0; i < selectorPluses.length; i++) {
      final ColumnSelectorPlus<InputRawSupplierColumnSelectorStrategy> selectorPlus = selectorPluses[i];
      final InputRawSupplierColumnSelectorStrategy strategy = selectorPlus.getColumnSelectorStrategy();
      inputRawSuppliers[i] = strategy.makeInputRawSupplier(selectorPlus.getSelector());
    }

    return inputRawSuppliers;
  }

  @SuppressWarnings("unchecked")
  private static Function<Comparable, Comparable>[] makeValueConvertFunctions(
      final List<ValueType> valueTypes
  )
  {
    final Function<Comparable, Comparable>[] functions = new Function[valueTypes.size()];
    for (int i = 0; i < functions.length; i++) {
      ValueType type = valueTypes.get(i);
      // Subquery post-aggs aren't added to the rowSignature (see rowSignatureFor() in GroupByQueryHelper) because
      // their types aren't known, so default to String handling.
      type = type == null ? ValueType.STRING : type;
      switch (type) {
        case STRING:
          functions[i] = input -> input == null ? "" : input.toString();
          break;

        case LONG:
          functions[i] = input -> {
            final Long val = DimensionHandlerUtils.convertObjectToLong(input);
            return val == null ? 0L : val;
          };
          break;

        case FLOAT:
          functions[i] = input -> {
            final Float val = DimensionHandlerUtils.convertObjectToFloat(input);
            return val == null ? 0.f : val;
          };
          break;

        case DOUBLE:
          functions[i] = input -> {
            Double val = DimensionHandlerUtils.convertObjectToDouble(input);
            return val == null ? 0.0 : val;
          };
          break;
        default:
          throw new IAE("invalid type: [%s]", type);
      }
    }
    return functions;
  }

  private static class RowBasedKeySerdeFactory implements Grouper.KeySerdeFactory<RowBasedKey>
  {
    private final boolean includeTimestamp;
    private final boolean sortByDimsFirst;
    private final int dimCount;
    private final long maxDictionarySize;
    private final DefaultLimitSpec limitSpec;
    private final List<DimensionSpec> dimensions;
    final AggregatorFactory[] aggregatorFactories;
    private final List<ValueType> valueTypes;

    RowBasedKeySerdeFactory(
        boolean includeTimestamp,
        boolean sortByDimsFirst,
        List<DimensionSpec> dimensions,
        long maxDictionarySize,
        List<ValueType> valueTypes,
        final AggregatorFactory[] aggregatorFactories,
        DefaultLimitSpec limitSpec
    )
    {
      this.includeTimestamp = includeTimestamp;
      this.sortByDimsFirst = sortByDimsFirst;
      this.dimensions = dimensions;
      this.dimCount = dimensions.size();
      this.maxDictionarySize = maxDictionarySize;
      this.limitSpec = limitSpec;
      this.aggregatorFactories = aggregatorFactories;
      this.valueTypes = valueTypes;
    }

    @Override
    public long getMaxDictionarySize()
    {
      return maxDictionarySize;
    }

    @Override
    public Grouper.KeySerde<RowBasedKey> factorize()
    {
      return new RowBasedKeySerde(
          includeTimestamp,
          sortByDimsFirst,
          dimensions,
          maxDictionarySize,
          limitSpec,
          valueTypes,
          null
      );
    }

    @Override
    public Grouper.KeySerde<RowBasedKey> factorizeWithDictionary(List<String> dictionary)
    {
      return new RowBasedKeySerde(
          includeTimestamp,
          sortByDimsFirst,
          dimensions,
          maxDictionarySize,
          limitSpec,
          valueTypes,
          dictionary
      );
    }

    @Override
    public Comparator<Grouper.Entry<RowBasedKey>> objectComparator(boolean forceDefaultOrder)
    {
      if (limitSpec != null && !forceDefaultOrder) {
        return objectComparatorWithAggs();
      }

      if (includeTimestamp) {
        if (sortByDimsFirst) {
          return new Comparator<Grouper.Entry<RowBasedKey>>()
          {
            @Override
            public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
            {
              final int cmp = compareDimsInRows(entry1.getKey(), entry2.getKey(), 1);
              if (cmp != 0) {
                return cmp;
              }

              return Longs.compare((long) entry1.getKey().getKey()[0], (long) entry2.getKey().getKey()[0]);
            }
          };
        } else {
          return new Comparator<Grouper.Entry<RowBasedKey>>()
          {
            @Override
            public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
            {
              final int timeCompare = Longs.compare(
                  (long) entry1.getKey().getKey()[0],
                  (long) entry2.getKey().getKey()[0]
              );

              if (timeCompare != 0) {
                return timeCompare;
              }

              return compareDimsInRows(entry1.getKey(), entry2.getKey(), 1);
            }
          };
        }
      } else {
        return new Comparator<Grouper.Entry<RowBasedKey>>()
        {
          @Override
          public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
          {
            return compareDimsInRows(entry1.getKey(), entry2.getKey(), 0);
          }
        };
      }
    }

    private Comparator<Grouper.Entry<RowBasedKey>> objectComparatorWithAggs()
    {
      // use the actual sort order from the limitspec if pushing down to merge partial results correctly
      final List<Boolean> needsReverses = Lists.newArrayList();
      final List<Boolean> aggFlags = Lists.newArrayList();
      final List<Boolean> isNumericField = Lists.newArrayList();
      final List<StringComparator> comparators = Lists.newArrayList();
      final List<Integer> fieldIndices = Lists.newArrayList();
      final Set<Integer> orderByIndices = new HashSet<>();

      for (OrderByColumnSpec orderSpec : limitSpec.getColumns()) {
        final boolean needsReverse = orderSpec.getDirection() != OrderByColumnSpec.Direction.ASCENDING;
        int dimIndex = OrderByColumnSpec.getDimIndexForOrderBy(orderSpec, dimensions);
        if (dimIndex >= 0) {
          fieldIndices.add(dimIndex);
          orderByIndices.add(dimIndex);
          needsReverses.add(needsReverse);
          aggFlags.add(false);
          final ValueType type = dimensions.get(dimIndex).getOutputType();
          isNumericField.add(ValueType.isNumeric(type));
          comparators.add(orderSpec.getDimensionComparator());
        } else {
          int aggIndex = OrderByColumnSpec.getAggIndexForOrderBy(orderSpec, Arrays.asList(aggregatorFactories));
          if (aggIndex >= 0) {
            fieldIndices.add(aggIndex);
            needsReverses.add(needsReverse);
            aggFlags.add(true);
            final String typeName = aggregatorFactories[aggIndex].getTypeName();
            isNumericField.add(ValueType.isNumeric(ValueType.fromString(typeName)));
            comparators.add(orderSpec.getDimensionComparator());
          }
        }
      }

      for (int i = 0; i < dimCount; i++) {
        if (!orderByIndices.contains(i)) {
          fieldIndices.add(i);
          aggFlags.add(false);
          needsReverses.add(false);
          final ValueType type = dimensions.get(i).getOutputType();
          isNumericField.add(ValueType.isNumeric(type));
          comparators.add(StringComparators.LEXICOGRAPHIC);
        }
      }

      if (includeTimestamp) {
        if (sortByDimsFirst) {
          return new Comparator<Grouper.Entry<RowBasedKey>>()
          {
            @Override
            public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
            {
              final int cmp = compareDimsInRowsWithAggs(
                  entry1,
                  entry2,
                  1,
                  needsReverses,
                  aggFlags,
                  fieldIndices,
                  isNumericField,
                  comparators
              );
              if (cmp != 0) {
                return cmp;
              }

              return Longs.compare((long) entry1.getKey().getKey()[0], (long) entry2.getKey().getKey()[0]);
            }
          };
        } else {
          return new Comparator<Grouper.Entry<RowBasedKey>>()
          {
            @Override
            public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
            {
              final int timeCompare = Longs.compare((long) entry1.getKey().getKey()[0], (long) entry2.getKey().getKey()[0]);

              if (timeCompare != 0) {
                return timeCompare;
              }

              return compareDimsInRowsWithAggs(
                  entry1,
                  entry2,
                  1,
                  needsReverses,
                  aggFlags,
                  fieldIndices,
                  isNumericField,
                  comparators
              );
            }
          };
        }
      } else {
        return new Comparator<Grouper.Entry<RowBasedKey>>()
        {
          @Override
          public int compare(Grouper.Entry<RowBasedKey> entry1, Grouper.Entry<RowBasedKey> entry2)
          {
            return compareDimsInRowsWithAggs(
                entry1,
                entry2,
                0,
                needsReverses,
                aggFlags,
                fieldIndices,
                isNumericField,
                comparators
            );
          }
        };
      }
    }

    private static int compareDimsInRows(RowBasedKey key1, RowBasedKey key2, int dimStart)
    {
      for (int i = dimStart; i < key1.getKey().length; i++) {
        final int cmp = ((Comparable) key1.getKey()[i]).compareTo(key2.getKey()[i]);
        if (cmp != 0) {
          return cmp;
        }
      }

      return 0;
    }

    private static int compareDimsInRowsWithAggs(
        Grouper.Entry<RowBasedKey> entry1,
        Grouper.Entry<RowBasedKey> entry2,
        int dimStart,
        final List<Boolean> needsReverses,
        final List<Boolean> aggFlags,
        final List<Integer> fieldIndices,
        final List<Boolean> isNumericField,
        final List<StringComparator> comparators
    )
    {
      for (int i = 0; i < fieldIndices.size(); i++) {
        final int fieldIndex = fieldIndices.get(i);
        final boolean needsReverse = needsReverses.get(i);
        final int cmp;
        final Comparable lhs;
        final Comparable rhs;

        if (aggFlags.get(i)) {
          if (needsReverse) {
            lhs = (Comparable) entry2.getValues()[fieldIndex];
            rhs = (Comparable) entry1.getValues()[fieldIndex];
          } else {
            lhs = (Comparable) entry1.getValues()[fieldIndex];
            rhs = (Comparable) entry2.getValues()[fieldIndex];
          }
        } else {
          if (needsReverse) {
            lhs = (Comparable) entry2.getKey().getKey()[fieldIndex + dimStart];
            rhs = (Comparable) entry1.getKey().getKey()[fieldIndex + dimStart];
          } else {
            lhs = (Comparable) entry1.getKey().getKey()[fieldIndex + dimStart];
            rhs = (Comparable) entry2.getKey().getKey()[fieldIndex + dimStart];
          }
        }

        final StringComparator comparator = comparators.get(i);

        if (isNumericField.get(i) && comparator.equals(StringComparators.NUMERIC)) {
          // use natural comparison
          cmp = lhs.compareTo(rhs);
        } else {
          cmp = comparator.compare(lhs.toString(), rhs.toString());
        }

        if (cmp != 0) {
          return cmp;
        }
      }

      return 0;
    }
  }

  static long estimateStringKeySize(String key)
  {
    return (long) key.length() * Character.BYTES + ROUGH_OVERHEAD_PER_DICTIONARY_ENTRY;
  }

  private static class RowBasedKeySerde implements Grouper.KeySerde<RowBasedGrouperHelper.RowBasedKey>
  {
    private static final int DICTIONARY_INITIAL_CAPACITY = 10000;
    private static final int UNKNOWN_DICTIONARY_ID = -1;

    private final boolean includeTimestamp;
    private final boolean sortByDimsFirst;
    private final List<DimensionSpec> dimensions;
    private final int dimCount;
    private final int keySize;
    private final ByteBuffer keyBuffer;
    private final RowBasedKeySerdeHelper[] serdeHelpers;
    private final BufferComparator[] serdeHelperComparators;
    private final DefaultLimitSpec limitSpec;
    private final List<ValueType> valueTypes;

    private final boolean enableRuntimeDictionaryGeneration;

    private final List<String> dictionary;
    private final Object2IntMap<String> reverseDictionary;

    // Size limiting for the dictionary, in (roughly estimated) bytes.
    private final long maxDictionarySize;

    private long currentEstimatedSize = 0;

    // dictionary id -> rank of the sorted dictionary
    // This is initialized in the constructor and bufferComparator() with static dictionary and dynamic dictionary,
    // respectively.
    private int[] rankOfDictionaryIds = null;

    RowBasedKeySerde(
        final boolean includeTimestamp,
        final boolean sortByDimsFirst,
        final List<DimensionSpec> dimensions,
        final long maxDictionarySize,
        final DefaultLimitSpec limitSpec,
        final List<ValueType> valueTypes,
        @Nullable final List<String> dictionary
    )
    {
      this.includeTimestamp = includeTimestamp;
      this.sortByDimsFirst = sortByDimsFirst;
      this.dimensions = dimensions;
      this.dimCount = dimensions.size();
      this.valueTypes = valueTypes;
      this.limitSpec = limitSpec;
      this.enableRuntimeDictionaryGeneration = dictionary == null;
      this.dictionary = enableRuntimeDictionaryGeneration ? new ArrayList<>(DICTIONARY_INITIAL_CAPACITY) : dictionary;
      this.reverseDictionary = enableRuntimeDictionaryGeneration ?
                               new Object2IntOpenHashMap<>(DICTIONARY_INITIAL_CAPACITY) :
                               new Object2IntOpenHashMap<>(dictionary.size());
      this.reverseDictionary.defaultReturnValue(UNKNOWN_DICTIONARY_ID);
      this.maxDictionarySize = maxDictionarySize;
      this.serdeHelpers = makeSerdeHelpers(limitSpec != null, enableRuntimeDictionaryGeneration);
      this.serdeHelperComparators = new BufferComparator[serdeHelpers.length];
      Arrays.setAll(serdeHelperComparators, i -> serdeHelpers[i].getBufferComparator());
      this.keySize = (includeTimestamp ? Long.BYTES : 0) + getTotalKeySize();
      this.keyBuffer = ByteBuffer.allocate(keySize);

      if (!enableRuntimeDictionaryGeneration) {
        final long initialDictionarySize = dictionary.stream()
                                                     .mapToLong(RowBasedGrouperHelper::estimateStringKeySize)
                                                     .sum();
        Preconditions.checkState(
            maxDictionarySize >= initialDictionarySize,
            "Dictionary size[%s] exceeds threshold[%s]",
            initialDictionarySize,
            maxDictionarySize
        );

        for (int i = 0; i < dictionary.size(); i++) {
          reverseDictionary.put(dictionary.get(i), i);
        }

        initializeRankOfDictionaryIds();
      }
    }

    private void initializeRankOfDictionaryIds()
    {
      final int dictionarySize = dictionary.size();
      rankOfDictionaryIds = IntStream.range(0, dictionarySize).toArray();
      IntArrays.quickSort(
          rankOfDictionaryIds,
          (i1, i2) -> dictionary.get(i1).compareTo(dictionary.get(i2))
      );

      IntArrayUtils.inverse(rankOfDictionaryIds);
    }

    @Override
    public int keySize()
    {
      return keySize;
    }

    @Override
    public Class<RowBasedKey> keyClazz()
    {
      return RowBasedKey.class;
    }

    @Override
    public List<String> getDictionary()
    {
      return dictionary;
    }

    @Override
    public ByteBuffer toByteBuffer(RowBasedKey key)
    {
      keyBuffer.rewind();

      final int dimStart;
      if (includeTimestamp) {
        keyBuffer.putLong((long) key.getKey()[0]);
        dimStart = 1;
      } else {
        dimStart = 0;
      }
      for (int i = dimStart; i < key.getKey().length; i++) {
        if (!serdeHelpers[i - dimStart].putToKeyBuffer(key, i)) {
          return null;
        }
      }

      keyBuffer.flip();
      return keyBuffer;
    }

    @Override
    public RowBasedKey fromByteBuffer(ByteBuffer buffer, int position)
    {
      final int dimStart;
      final Comparable[] key;
      final int dimsPosition;

      if (includeTimestamp) {
        key = new Comparable[dimCount + 1];
        key[0] = buffer.getLong(position);
        dimsPosition = position + Long.BYTES;
        dimStart = 1;
      } else {
        key = new Comparable[dimCount];
        dimsPosition = position;
        dimStart = 0;
      }

      for (int i = dimStart; i < key.length; i++) {
        // Writes value from buffer to key[i]
        serdeHelpers[i - dimStart].getFromByteBuffer(buffer, dimsPosition, i, key);
      }

      return new RowBasedKey(key);
    }

    @Override
    public Grouper.BufferComparator bufferComparator()
    {
      if (rankOfDictionaryIds == null) {
        initializeRankOfDictionaryIds();
      }

      if (includeTimestamp) {
        if (sortByDimsFirst) {
          return new Grouper.BufferComparator()
          {
            @Override
            public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
            {
              final int cmp = compareDimsInBuffersForNullFudgeTimestamp(
                  serdeHelperComparators,
                  lhsBuffer,
                  rhsBuffer,
                  lhsPosition,
                  rhsPosition
              );
              if (cmp != 0) {
                return cmp;
              }

              return Longs.compare(lhsBuffer.getLong(lhsPosition), rhsBuffer.getLong(rhsPosition));
            }
          };
        } else {
          return new Grouper.BufferComparator()
          {
            @Override
            public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
            {
              final int timeCompare = Longs.compare(lhsBuffer.getLong(lhsPosition), rhsBuffer.getLong(rhsPosition));

              if (timeCompare != 0) {
                return timeCompare;
              }

              return compareDimsInBuffersForNullFudgeTimestamp(
                  serdeHelperComparators,
                  lhsBuffer,
                  rhsBuffer,
                  lhsPosition,
                  rhsPosition
              );
            }
          };
        }
      } else {
        return new Grouper.BufferComparator()
        {
          @Override
          public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
          {
            for (int i = 0; i < dimCount; i++) {
              final int cmp = serdeHelperComparators[i].compare(
                  lhsBuffer,
                  rhsBuffer,
                  lhsPosition,
                  rhsPosition
              );

              if (cmp != 0) {
                return cmp;
              }
            }

            return 0;
          }
        };
      }
    }

    @Override
    public Grouper.BufferComparator bufferComparatorWithAggregators(
        AggregatorFactory[] aggregatorFactories,
        int[] aggregatorOffsets
    )
    {
      final List<RowBasedKeySerdeHelper> adjustedSerdeHelpers;
      final List<Boolean> needsReverses = Lists.newArrayList();
      List<RowBasedKeySerdeHelper> orderByHelpers = new ArrayList<>();
      List<RowBasedKeySerdeHelper> otherDimHelpers = new ArrayList<>();
      Set<Integer> orderByIndices = new HashSet<>();

      int aggCount = 0;
      boolean needsReverse;
      for (OrderByColumnSpec orderSpec : limitSpec.getColumns()) {
        needsReverse = orderSpec.getDirection() != OrderByColumnSpec.Direction.ASCENDING;
        int dimIndex = OrderByColumnSpec.getDimIndexForOrderBy(orderSpec, dimensions);
        if (dimIndex >= 0) {
          RowBasedKeySerdeHelper serdeHelper = serdeHelpers[dimIndex];
          orderByHelpers.add(serdeHelper);
          orderByIndices.add(dimIndex);
          needsReverses.add(needsReverse);
        } else {
          int aggIndex = OrderByColumnSpec.getAggIndexForOrderBy(orderSpec, Arrays.asList(aggregatorFactories));
          if (aggIndex >= 0) {
            final RowBasedKeySerdeHelper serdeHelper;
            final StringComparator stringComparator = orderSpec.getDimensionComparator();
            final String typeName = aggregatorFactories[aggIndex].getTypeName();
            final int aggOffset = aggregatorOffsets[aggIndex] - Integer.BYTES;

            aggCount++;

            final ValueType valueType = ValueType.fromString(typeName);
            if (!ValueType.isNumeric(valueType)) {
              throw new IAE("Cannot order by a non-numeric aggregator[%s]", orderSpec);
            }

            serdeHelper = makeNumericSerdeHelper(valueType, aggOffset, true, stringComparator);

            orderByHelpers.add(serdeHelper);
            needsReverses.add(needsReverse);
          }
        }
      }

      for (int i = 0; i < dimCount; i++) {
        if (!orderByIndices.contains(i)) {
          otherDimHelpers.add(serdeHelpers[i]);
          needsReverses.add(false); // default to Ascending order if dim is not in an orderby spec
        }
      }

      adjustedSerdeHelpers = orderByHelpers;
      adjustedSerdeHelpers.addAll(otherDimHelpers);

      final BufferComparator[] adjustedSerdeHelperComparators = new BufferComparator[adjustedSerdeHelpers.size()];
      Arrays.setAll(adjustedSerdeHelperComparators, i -> adjustedSerdeHelpers.get(i).getBufferComparator());

      final int fieldCount = dimCount + aggCount;

      if (includeTimestamp) {
        if (sortByDimsFirst) {
          return new Grouper.BufferComparator()
          {
            @Override
            public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
            {
              final int cmp = compareDimsInBuffersForNullFudgeTimestampForPushDown(
                  adjustedSerdeHelperComparators,
                  needsReverses,
                  fieldCount,
                  lhsBuffer,
                  rhsBuffer,
                  lhsPosition,
                  rhsPosition
              );
              if (cmp != 0) {
                return cmp;
              }

              return Longs.compare(lhsBuffer.getLong(lhsPosition), rhsBuffer.getLong(rhsPosition));
            }
          };
        } else {
          return new Grouper.BufferComparator()
          {
            @Override
            public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
            {
              final int timeCompare = Longs.compare(lhsBuffer.getLong(lhsPosition), rhsBuffer.getLong(rhsPosition));

              if (timeCompare != 0) {
                return timeCompare;
              }

              int cmp = compareDimsInBuffersForNullFudgeTimestampForPushDown(
                  adjustedSerdeHelperComparators,
                  needsReverses,
                  fieldCount,
                  lhsBuffer,
                  rhsBuffer,
                  lhsPosition,
                  rhsPosition
              );

              return cmp;
            }
          };
        }
      } else {
        return new Grouper.BufferComparator()
        {
          @Override
          public int compare(ByteBuffer lhsBuffer, ByteBuffer rhsBuffer, int lhsPosition, int rhsPosition)
          {
            for (int i = 0; i < fieldCount; i++) {
              final int cmp;
              if (needsReverses.get(i)) {
                cmp = adjustedSerdeHelperComparators[i].compare(
                    rhsBuffer,
                    lhsBuffer,
                    rhsPosition,
                    lhsPosition
                );
              } else {
                cmp = adjustedSerdeHelperComparators[i].compare(
                    lhsBuffer,
                    rhsBuffer,
                    lhsPosition,
                    rhsPosition
                );
              }

              if (cmp != 0) {
                return cmp;
              }
            }

            return 0;
          }
        };
      }
    }

    @Override
    public void reset()
    {
      if (enableRuntimeDictionaryGeneration) {
        dictionary.clear();
        reverseDictionary.clear();
        rankOfDictionaryIds = null;
        currentEstimatedSize = 0;
      }
    }

    private int getTotalKeySize()
    {
      int size = 0;
      for (RowBasedKeySerdeHelper helper : serdeHelpers) {
        size += helper.getKeyBufferValueSize();
      }
      return size;
    }

    private RowBasedKeySerdeHelper[] makeSerdeHelpers(
        boolean pushLimitDown,
        boolean enableRuntimeDictionaryGeneration
    )
    {
      final List<RowBasedKeySerdeHelper> helpers = new ArrayList<>();
      int keyBufferPosition = 0;

      for (int i = 0; i < dimCount; i++) {
        final StringComparator stringComparator;
        if (limitSpec != null) {
          final String dimName = dimensions.get(i).getOutputName();
          stringComparator = DefaultLimitSpec.getComparatorForDimName(limitSpec, dimName);
        } else {
          stringComparator = null;
        }

        RowBasedKeySerdeHelper helper = makeSerdeHelper(
            valueTypes.get(i),
            keyBufferPosition,
            pushLimitDown,
            stringComparator,
            enableRuntimeDictionaryGeneration
        );

        keyBufferPosition += helper.getKeyBufferValueSize();
        helpers.add(helper);
      }

      return helpers.toArray(new RowBasedKeySerdeHelper[helpers.size()]);
    }

    private RowBasedKeySerdeHelper makeSerdeHelper(
        ValueType valueType,
        int keyBufferPosition,
        boolean pushLimitDown,
        @Nullable StringComparator stringComparator,
        boolean enableRuntimeDictionaryGeneration
    )
    {
      switch (valueType) {
        case STRING:
          if (enableRuntimeDictionaryGeneration) {
            return new DynamicDictionaryStringRowBasedKeySerdeHelper(
                keyBufferPosition,
                pushLimitDown,
                stringComparator
            );
          } else {
            return new StaticDictionaryStringRowBasedKeySerdeHelper(
                keyBufferPosition,
                pushLimitDown,
                stringComparator
            );
          }
        case LONG:
        case FLOAT:
        case DOUBLE:
          return makeNumericSerdeHelper(valueType, keyBufferPosition, pushLimitDown, stringComparator);
        default:
          throw new IAE("invalid type: %s", valueType);
      }
    }

    private RowBasedKeySerdeHelper makeNumericSerdeHelper(
        ValueType valueType,
        int keyBufferPosition,
        boolean pushLimitDown,
        @Nullable StringComparator stringComparator
    )
    {
      switch (valueType) {
        case LONG:
          return new LongRowBasedKeySerdeHelper(keyBufferPosition, pushLimitDown, stringComparator);
        case FLOAT:
          return new FloatRowBasedKeySerdeHelper(keyBufferPosition, pushLimitDown, stringComparator);
        case DOUBLE:
          return new DoubleRowBasedKeySerdeHelper(keyBufferPosition, pushLimitDown, stringComparator);
        default:
          throw new IAE("invalid type: %s", valueType);
      }
    }

    private static boolean isPrimitiveComparable(boolean pushLimitDown, @Nullable StringComparator stringComparator)
    {
      return !pushLimitDown || stringComparator == null || stringComparator.equals(StringComparators.NUMERIC);
    }

    private abstract class AbstractStringRowBasedKeySerdeHelper implements RowBasedKeySerdeHelper
    {
      final int keyBufferPosition;

      final BufferComparator bufferComparator;

      AbstractStringRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator
      )
      {
        this.keyBufferPosition = keyBufferPosition;
        if (!pushLimitDown) {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> Ints.compare(
              rankOfDictionaryIds[lhsBuffer.getInt(lhsPosition + keyBufferPosition)],
              rankOfDictionaryIds[rhsBuffer.getInt(rhsPosition + keyBufferPosition)]
          );
        } else {
          final StringComparator realComparator = stringComparator == null ?
                                                  StringComparators.LEXICOGRAPHIC :
                                                  stringComparator;
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> {
            String lhsStr = dictionary.get(lhsBuffer.getInt(lhsPosition + keyBufferPosition));
            String rhsStr = dictionary.get(rhsBuffer.getInt(rhsPosition + keyBufferPosition));
            return realComparator.compare(lhsStr, rhsStr);
          };
        }
      }

      @Override
      public int getKeyBufferValueSize()
      {
        return Integer.BYTES;
      }

      @Override
      public void getFromByteBuffer(ByteBuffer buffer, int initialOffset, int dimValIdx, Comparable[] dimValues)
      {
        dimValues[dimValIdx] = dictionary.get(buffer.getInt(initialOffset + keyBufferPosition));
      }

      @Override
      public BufferComparator getBufferComparator()
      {
        return bufferComparator;
      }
    }

    private class DynamicDictionaryStringRowBasedKeySerdeHelper extends AbstractStringRowBasedKeySerdeHelper
    {
      DynamicDictionaryStringRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator
      )
      {
        super(keyBufferPosition, pushLimitDown, stringComparator);
      }

      @Override
      public boolean putToKeyBuffer(RowBasedKey key, int idx)
      {
        final int id = addToDictionary((String) key.getKey()[idx]);
        if (id < 0) {
          return false;
        }
        keyBuffer.putInt(id);
        return true;
      }

      /**
       * Adds s to the dictionary. If the dictionary's size limit would be exceeded by adding this key, then
       * this returns -1.
       *
       * @param s a string
       *
       * @return id for this string, or -1
       */
      private int addToDictionary(final String s)
      {
        int idx = reverseDictionary.getInt(s);
        if (idx == UNKNOWN_DICTIONARY_ID) {
          final long additionalEstimatedSize = estimateStringKeySize(s);
          if (currentEstimatedSize + additionalEstimatedSize > maxDictionarySize) {
            return -1;
          }

          idx = dictionary.size();
          reverseDictionary.put(s, idx);
          dictionary.add(s);
          currentEstimatedSize += additionalEstimatedSize;
        }
        return idx;
      }
    }

    private class StaticDictionaryStringRowBasedKeySerdeHelper extends AbstractStringRowBasedKeySerdeHelper
    {
      StaticDictionaryStringRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator
      )
      {
        super(keyBufferPosition, pushLimitDown, stringComparator);
      }

      @Override
      public boolean putToKeyBuffer(RowBasedKey key, int idx)
      {
        final String stringKey = (String) key.getKey()[idx];

        final int dictIndex = reverseDictionary.getInt(stringKey);
        if (dictIndex == UNKNOWN_DICTIONARY_ID) {
          throw new ISE("Cannot find key[%s] from dictionary", stringKey);
        }
        keyBuffer.putInt(dictIndex);
        return true;
      }
    }

    private class LongRowBasedKeySerdeHelper implements RowBasedKeySerdeHelper
    {
      final int keyBufferPosition;
      final BufferComparator bufferComparator;

      LongRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator
      )
      {
        this.keyBufferPosition = keyBufferPosition;
        if (isPrimitiveComparable(pushLimitDown, stringComparator)) {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> Longs.compare(
              lhsBuffer.getLong(lhsPosition + keyBufferPosition),
              rhsBuffer.getLong(rhsPosition + keyBufferPosition)
          );
        } else {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> {
            long lhs = lhsBuffer.getLong(lhsPosition + keyBufferPosition);
            long rhs = rhsBuffer.getLong(rhsPosition + keyBufferPosition);

            return stringComparator.compare(String.valueOf(lhs), String.valueOf(rhs));
          };
        }
      }

      @Override
      public int getKeyBufferValueSize()
      {
        return Long.BYTES;
      }

      @Override
      public boolean putToKeyBuffer(RowBasedKey key, int idx)
      {
        keyBuffer.putLong((Long) key.getKey()[idx]);
        return true;
      }

      @Override
      public void getFromByteBuffer(ByteBuffer buffer, int initialOffset, int dimValIdx, Comparable[] dimValues)
      {
        dimValues[dimValIdx] = buffer.getLong(initialOffset + keyBufferPosition);
      }

      @Override
      public BufferComparator getBufferComparator()
      {
        return bufferComparator;
      }
    }

    private class FloatRowBasedKeySerdeHelper implements RowBasedKeySerdeHelper
    {
      final int keyBufferPosition;
      final BufferComparator bufferComparator;

      FloatRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator)
      {
        this.keyBufferPosition = keyBufferPosition;
        if (isPrimitiveComparable(pushLimitDown, stringComparator)) {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> Float.compare(
              lhsBuffer.getFloat(lhsPosition + keyBufferPosition),
              rhsBuffer.getFloat(rhsPosition + keyBufferPosition)
          );
        } else {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> {
            float lhs = lhsBuffer.getFloat(lhsPosition + keyBufferPosition);
            float rhs = rhsBuffer.getFloat(rhsPosition + keyBufferPosition);
            return stringComparator.compare(String.valueOf(lhs), String.valueOf(rhs));
          };
        }
      }

      @Override
      public int getKeyBufferValueSize()
      {
        return Float.BYTES;
      }

      @Override
      public boolean putToKeyBuffer(RowBasedKey key, int idx)
      {
        keyBuffer.putFloat((Float) key.getKey()[idx]);
        return true;
      }

      @Override
      public void getFromByteBuffer(ByteBuffer buffer, int initialOffset, int dimValIdx, Comparable[] dimValues)
      {
        dimValues[dimValIdx] = buffer.getFloat(initialOffset + keyBufferPosition);
      }

      @Override
      public BufferComparator getBufferComparator()
      {
        return bufferComparator;
      }
    }

    private class DoubleRowBasedKeySerdeHelper implements RowBasedKeySerdeHelper
    {
      final int keyBufferPosition;
      final BufferComparator bufferComparator;

      DoubleRowBasedKeySerdeHelper(
          int keyBufferPosition,
          boolean pushLimitDown,
          @Nullable StringComparator stringComparator
      )
      {
        this.keyBufferPosition = keyBufferPosition;
        if (isPrimitiveComparable(pushLimitDown, stringComparator)) {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> Double.compare(
              lhsBuffer.getDouble(lhsPosition + keyBufferPosition),
              rhsBuffer.getDouble(rhsPosition + keyBufferPosition)
          );
        } else {
          bufferComparator = (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> {
            double lhs = lhsBuffer.getDouble(lhsPosition + keyBufferPosition);
            double rhs = rhsBuffer.getDouble(rhsPosition + keyBufferPosition);
            return stringComparator.compare(String.valueOf(lhs), String.valueOf(rhs));
          };
        }
      }

      @Override
      public int getKeyBufferValueSize()
      {
        return Double.BYTES;
      }

      @Override
      public boolean putToKeyBuffer(RowBasedKey key, int idx)
      {
        keyBuffer.putDouble((Double) key.getKey()[idx]);
        return true;
      }

      @Override
      public void getFromByteBuffer(ByteBuffer buffer, int initialOffset, int dimValIdx, Comparable[] dimValues)
      {
        dimValues[dimValIdx] = buffer.getDouble(initialOffset + keyBufferPosition);
      }

      @Override
      public BufferComparator getBufferComparator()
      {
        return bufferComparator;
      }
    }
  }

  private static int compareDimsInBuffersForNullFudgeTimestamp(
      BufferComparator[] serdeHelperComparators,
      ByteBuffer lhsBuffer,
      ByteBuffer rhsBuffer,
      int lhsPosition,
      int rhsPosition
  )
  {
    for (BufferComparator comparator : serdeHelperComparators) {
      final int cmp = comparator.compare(
          lhsBuffer,
          rhsBuffer,
          lhsPosition + Long.BYTES,
          rhsPosition + Long.BYTES
      );
      if (cmp != 0) {
        return cmp;
      }
    }

    return 0;
  }

  private static int compareDimsInBuffersForNullFudgeTimestampForPushDown(
      BufferComparator[] serdeHelperComparators,
      List<Boolean> needsReverses,
      int dimCount,
      ByteBuffer lhsBuffer,
      ByteBuffer rhsBuffer,
      int lhsPosition,
      int rhsPosition
  )
  {
    for (int i = 0; i < dimCount; i++) {
      final int cmp;
      if (needsReverses.get(i)) {
        cmp = serdeHelperComparators[i].compare(
            rhsBuffer,
            lhsBuffer,
            rhsPosition + Long.BYTES,
            lhsPosition + Long.BYTES
        );
      } else {
        cmp = serdeHelperComparators[i].compare(
            lhsBuffer,
            rhsBuffer,
            lhsPosition + Long.BYTES,
            rhsPosition + Long.BYTES
        );
      }
      if (cmp != 0) {
        return cmp;
      }
    }

    return 0;
  }
}


text:
function getScrollbarWidth() {
	var outer = document.createElement("div");
	outer.style.visibility = "hidden";
	outer.style.width = "100px";
	outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps

	document.body.appendChild(outer);

	var widthNoScroll = outer.offsetWidth;
	// force scrollbars
	outer.style.overflow = "scroll";

	// add innerdiv
	var inner = document.createElement("div");
	inner.style.width = "100%";
	outer.appendChild(inner);

	var widthWithScroll = inner.offsetWidth;

	// remove divs
	outer.parentNode.removeChild(outer);

	return widthNoScroll - widthWithScroll;
}

document.body.className += " " + "scroll-width-" + getScrollbarWidth();


text:
const fs     = require('fs'),
      koa    = require('koa'),
      serve  = require('koa-static'),
      config = require('../config');

const app = koa();

// ------------------------------------
// Response Time Header and Logging
// ------------------------------------
app.use(require('./middleware/response-time'));
app.use(require('./middleware/logger'));

// ------------------------------------
// Static File Middleware
// ------------------------------------
app.use(serve(config.inDist('client'), {
  index : '__IGNORE_INDEX.HTML__'
}));

// ------------------------------------
// View Rendering
// ------------------------------------
const template = fs.readFileSync(config.inDist('client/index.html'), 'utf-8')
  .replace('

', '
${content}
');

app.use(require('./middleware/render-route')(template));

module.exports = app;


text:
<?php
namespace Chamilo\Application\Portfolio\Integration\Chamilo\Core\Repository\ContentObject\Portfolio\Package;

/**
 * Package installer
 *
 * @package application\portfolio\integration\repository\content_object\portfolio
 * @author Hans De Bisschop <hans.de.bisschop@ehb.be>
 */
class Installer extends \Chamilo\Configuration\Package\Action\Installer
{
}


text:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GitHub.Models;
using Octokit;

namespace GitHub.Services
{
    /// <summary>
    /// A pull request session used to display inline comments.
    /// </summary>
    public interface IPullRequestSession
    {
        /// <summary>
        /// Gets a value indicating whether the pull request branch is the currently checked out branch.
        /// </summary>
        bool IsCheckedOut { get; }

        /// <summary>
        /// Gets the current user.
        /// </summary>
        ActorModel User { get; }

        /// <summary>
        /// Gets the pull request.
        /// </summary>
        PullRequestDetailModel PullRequest { get; }

        /// <summary>
        /// Gets an observable that indicates that<see cref="PullRequest"/> has been updated.
        /// </summary>
        /// <remarks>
        /// This notification is different to listening for a PropertyChanged event because the
        /// pull request model may be updated in-place which will not result in a PropertyChanged
        /// notification.
        /// </remarks>
        IObservable<PullRequestDetailModel> PullRequestChanged { get; }

        /// <summary>
        /// Gets the local repository.
        /// </summary>
        LocalRepositoryModel LocalRepository { get; }

        /// <summary>
        /// Gets the owner of the repository that contains the pull request.
        /// </summary>
        /// <remarks>
        /// If the pull request is targeting <see cref="LocalRepository"/> then the owner will be
        /// the owner of the local repository. If however the pull request targets a different fork
        /// then this property describes the owner of the fork.
        /// </remarks>
        string RepositoryOwner { get; }

        /// <summary>
        /// Gets a value indicating whether the pull request has a pending review for the current
        /// user.
        /// </summary>
        bool HasPendingReview { get; }

        /// <summary>
        /// Gets the ID of the current pending pull request review for the user.
        /// </summary>
        string PendingReviewId { get; }

        /// <summary>
        /// Gets all files touched by the pull request.
        /// </summary>
        /// <returns>
        /// A list of the files touched by the pull request.
        /// </returns>
        Task<IReadOnlyList<IPullRequestSessionFile>> GetAllFiles();

        /// <summary>
        /// Gets a file touched by the pull request.
        /// </summary>
        /// <param name="relativePath">The relative path to the file.</param>
        /// <param name="commitSha">
        /// The commit at which to get the file contents, or "HEAD" to track the pull request head.
        /// </param>
        /// <returns>
        /// A <see cref="IPullRequestSessionFile"/> object or null if the file was not touched by
        /// the pull request.
        /// </returns>
        Task<IPullRequestSessionFile> GetFile(string relativePath, string commitSha = "HEAD");

        /// <summary>
        /// Gets the merge base SHA for the pull request.
        /// </summary>
        /// <returns>The merge base SHA.</returns>
        Task<string> GetMergeBase();

        /// <summary>
        /// Posts a new PR review comment.
        /// </summary>
        /// <param name="body">The comment body.</param>
        /// <param name="commitId">THe SHA of the commit to comment on.</param>
        /// <param name="path">The relative path of the file to comment on.</param>
        /// <param name="fileDiff">The diff between the PR head and base.</param>
        /// <param name="position">The line index in the diff to comment on.</param>
        Task PostReviewComment(
            string body,
            string commitId,
            string path,
            IReadOnlyList<DiffChunk> fileDiff,
            int position);

        /// <summary>
        /// Posts a PR review comment reply.
        /// </summary>
        /// <param name="body">The comment body.</param>
        /// <param name="inReplyTo">The GraphQL ID of the comment to reply to.</param>
        /// <returns></returns>
        Task PostReviewComment(
            string body,
            string inReplyTo);

        /// <summary>
        /// Starts a new pending pull request review.
        /// </summary>
        Task StartReview();

        /// <summary>
        /// Cancels the currently pending review.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// There is no pending review.
        /// </exception>
        Task CancelReview();

        /// <summary>
        /// Posts the currently pending review.
        /// </summary>
        /// <param name="body">The review body.</param>
        /// <param name="e">The review event.</param>
        /// <returns>The review model.</returns>
        Task PostReview(string body, PullRequestReviewEvent e);

        /// <summary>
        /// Deletes a pull request comment.
        /// </summary>
        /// <param name="pullRequestId">The number of the pull request id of the comment</param>
        /// <param name="commentDatabaseId">The number of the pull request comment to delete</param>
        /// <returns>A task which completes when the session has completed updating.</returns>
        Task DeleteComment(int pullRequestId, int commentDatabaseId);

        /// <summary>
        /// Edit a PR review comment reply.
        /// </summary>
        /// <param name="commentNodeId">The node id of the pull request comment</param>
        /// <param name="body">The replacement comment body.</param>
        /// <returns>A comment model.</returns>
        Task EditComment(string commentNodeId, string body);

        /// <summary>
        /// Refreshes the pull request session.
        /// </summary>
        /// <returns>A task which completes when the session has completed refreshing.</returns>
        Task Refresh();
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
    <soap:Body>
        <GetFolder xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
                   xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
            <FolderShape>
                <t:BaseShape>Default</t:BaseShape>
            </FolderShape>
            <FolderIds>
                <t:DistinguishedFolderId Id="{0}"/>
            </FolderIds>
        </GetFolder>
    </soap:Body>
</soap:Envelope>


text:
var createPool = require("@nathanfaucett/create_pool"),
    consts = require("./consts");

var OrderPatchPrototype;

module.exports = OrderPatch;

function OrderPatch() {
    this.type = consts.ORDER;
    this.id = null;
    this.order = null;
}
createPool(OrderPatch);
OrderPatchPrototype = OrderPatch.prototype;

OrderPatch.create = function(id, order) {
    var patch = OrderPatch.getPooled();
    patch.id = id;
    patch.order = order;
    return patch;
};

OrderPatchPrototype.destructor = function() {
    this.id = null;
    this.order = null;
    return this;
};

OrderPatchPrototype.destroy = function() {
    return OrderPatch.release(this);
};


text:
<?xml version="1.0" encoding="UTF-8"?><resources>

    <string name="app_name">Диалог выбора файла Parian</string>
    <string name="action_settings">Настройки</string>
    <string name="ok">Выбрать</string>
    <string name="files">Файлы</string>
    <string name="directories">Папки</string>
    <string name="goUp">Вверх</string>
    <string name="all">Отметить все</string>
    <string name="cancel">Отмена</string>
    <string name="none">Снять отметку со всех</string>
    <string name="ext">Карта памяти</string>
    <string name="Int">Внутренняя</string>
    <string name="New">Новая папка</string>
    <string name="CNew">Ввести имя новой папки</string>
    <string name="create">Создать</string>

</resources>


text:
using namespace Windows::Foundation;
using namespace Microsoft::Xbox::Services;
using namespace Microsoft::Xbox::Services::System;

NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_BEGIN

MultiplayerManagedInitialization::MultiplayerManagedInitialization(
    _In_ xbox::services::multiplayer::multiplayer_managed_initialization cppObj
    ) :
    m_cppObj(cppObj)
{
}

NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_END