text:
/**
 * \file  mcaspPlayBk.c
 *
 * \brief McASP example application
 *
 */

/*
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
*/
/*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*    Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the
*    distribution.
*
*    Neither the name of Texas Instruments Incorporated nor the names of
*    its contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

#include "edma_event.h"
#include "interrupt.h"
#include "soc_AM335x.h"
#include "evmskAM335x.h"
#include "codecif.h"
#include "mcasp.h"
#include "aic31.h"
#include "edma.h"
#include "uartStdio.h"

#include <string.h>

/******************************************************************************
**                      INTERNAL MACRO DEFINITIONS
******************************************************************************/
/*
** Values which are configurable
*/
/* Slot size to send/receive data */
#define SLOT_SIZE                             (16u)

#define I2S_SLOTS_L_R                 (0x03u)

/* Word size to send/receive data. Word size <= Slot size */
#define WORD_SIZE                             (16u)

/* Sampling Rate which will be used by both transmit and receive sections */
#define SAMPLING_RATE                         (48000u)

/* Number of channels, L & R */
#define NUM_I2S_CHANNELS                      (2u)

/* Number of samples to be used per audio buffer */
#define NUM_SAMPLES_PER_AUDIO_BUF             (2000u)

/* Number of buffers used per tx/rx */
#define NUM_BUF                               (3u)

/* Number of linked parameter set used per tx/rx */
#define NUM_PAR                               (6u)

/* Number of samples in loop buffer */
#define NUM_SAMPLES_LOOP_BUF                  (10u)

/* AIC3106 codec address */
#define I2C_SLAVE_CODEC_AIC31                 (0x1Bu)

/* Interrupt channels to map in AINTC */
#define INT_CHANNEL_I2C                       (2u)
#define INT_CHANNEL_MCASP                     (2u)
#define INT_CHANNEL_EDMACC                    (2u)

/* McASP Serializer for Transmit */
#define MCASP_XSER_TX                         (2u)

/*
** Below Macros are calculated based on the above inputs
*/
#define NUM_TX_SERIALIZERS                    ((NUM_I2S_CHANNELS >> 1) \
                                               + (NUM_I2S_CHANNELS & 0x01))
#define I2S_SLOTS                             ((1 << NUM_I2S_CHANNELS) - 1)

#define BYTES_PER_SAMPLE                      ((WORD_SIZE >> 3) \
                                               * NUM_I2S_CHANNELS)

#define AUDIO_BUF_SIZE                        (NUM_SAMPLES_PER_AUDIO_BUF \
                                               * BYTES_PER_SAMPLE)

#define TX_DMA_INT_ENABLE                     (EDMA3CC_OPT_TCC_SET(EDMA3_CHA_MCASP1_TX) | (1 \
                                               << EDMA3CC_OPT_TCINTEN_SHIFT))

#define PAR_TX_START                          (70 + NUM_PAR)

/*
** Definitions which are not configurable
*/
#define SIZE_PARAMSET                         (32u)
#define OPT_FIFO_WIDTH                        (0x02 << 8u)

//#define TONE_NUM_BYTES                        (sizeof(toneRaw))

/******************************************************************************
**                      INTERNAL FUNCTION PROTOTYPES
******************************************************************************/
void SoundClickPlay(unsigned char* toneBase, unsigned int toneSize);
static void AIC31I2SConfigure(void);
static void McASPI2SConfigure(void);
static void McASPTxDMAComplHandler(void);
static void EDMA3CCComplIsr(void);
static void I2SDataTxRxActivate(void);
static void I2SDMAParamInit(void);
static void ParamTxLoopJobSet(unsigned short parId);
static void BufferTxDMAActivate(unsigned int txBuf, unsigned short numSamples,
                                unsigned short parToUpdate,
                                unsigned short linkAddr);

extern unsigned short const toneRaw;
/******************************************************************************
**                      INTERNAL VARIABLE DEFINITIONS
******************************************************************************/
static unsigned char loopBuf[NUM_SAMPLES_LOOP_BUF * BYTES_PER_SAMPLE] = {0};

/*
** Transmit buffers. If any new buffer is to be added, define it here and
** update the NUM_BUF.
*/
static unsigned char txBuf0[AUDIO_BUF_SIZE];
static unsigned char txBuf1[AUDIO_BUF_SIZE];
static unsigned char txBuf2[AUDIO_BUF_SIZE];

/*
** The offset of the paRAM ID sent, from starting of the paRAM set.
*/
static volatile unsigned short parOffSent = NUM_PAR - 1;

/*
** The offset of the paRAM ID to be sent next, from starting of the paRAM set.
*/
static volatile unsigned short parOffTxToSend = 0;

/*
** The transmit buffer which was sent last.
*/
static volatile unsigned int lastSentTxBuf = NUM_BUF - 1;

/******************************************************************************
**                      INTERNAL CONSTATNT DEFINITIONS
******************************************************************************/
/*
** Default paRAM for Transmit section. This will be transmitting from
** a loop buffer.
*/
static struct EDMA3CCPaRAMEntry const txDefaultPar =
       {
           (unsigned int)(0x02 << 8u), /* Opt field */
           (unsigned int)loopBuf, /* source address */
           (unsigned short)(BYTES_PER_SAMPLE), /* aCnt */
           (unsigned short)(NUM_SAMPLES_LOOP_BUF), /* bCnt */
           (unsigned int) SOC_MCASP_1_DATA_REGS, /* dest address */
           (short) (BYTES_PER_SAMPLE), /* source bIdx */
           (short)(0), /* dest bIdx */
           (unsigned short)(PAR_TX_START * SIZE_PARAMSET), /* link address */
           (unsigned short)(0), /* bCnt reload value */
           (short)(0), /* source cIdx */
           (short)(0), /* dest cIdx */
           (unsigned short)1 /* cCnt */
       };

/* Array of transmit buffer pointers */
static unsigned int const txBufPtr[NUM_BUF] =
       {
           (unsigned int) txBuf0,
           (unsigned int) txBuf1,
           (unsigned int) txBuf2
       };

/******************************************************************************
**                          FUNCTION DEFINITIONS
******************************************************************************/
/*
** Assigns loop job for a parameter set
*/
static void ParamTxLoopJobSet(unsigned short parId)
{
    EDMA3CCPaRAMEntry paramSet;

    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET);

    /* link the paRAM to itself */
    paramSet.linkAddr = parId * SIZE_PARAMSET;

    EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, parId, &paramSet);
}

/*
** Initializes the DMA parameters.
*/
static void I2SDMAParamInit(void)
{
    EDMA3CCPaRAMEntry paramSet;
    int idx;

    /* Initialize the 1st paRAM set for transmit */
    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET);

    EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP1_TX, &paramSet);

    /* rest of the params, enable loop job */
    for(idx = 0 ; idx < NUM_PAR; idx++)
    {
        ParamTxLoopJobSet((unsigned short)(PAR_TX_START + idx));
    }

    /* Initialize the variables for transmit */
    parOffSent = NUM_PAR - 1;
    lastSentTxBuf = NUM_BUF - 1;
}

/*
** Function to configure the codec for I2S mode
*/
static void AIC31I2SConfigure(void)
{
    volatile unsigned int delay = 0xFFF;

    AIC31Reset(SOC_I2C_0_REGS);
    while(delay--);

    /* Configure the data format and sampling rate */
    AIC31DataConfig(SOC_I2C_0_REGS, AIC31_DATATYPE_I2S, SLOT_SIZE, 0);
    AIC31SampleRateConfig(SOC_I2C_0_REGS, AIC31_MODE_BOTH, SAMPLING_RATE);

    /* Initialize both ADC and DAC */
    AIC31ADCInit(SOC_I2C_0_REGS);
    AIC31DACInit(SOC_I2C_0_REGS);
}

/*
** Configures the McASP Transmit Section in I2S mode.
*/
static void McASPI2SConfigure(void)
{
    McASPTxReset(SOC_MCASP_1_CTRL_REGS);

    /* Enable the FIFOs for DMA transfer */
    McASPWriteFifoEnable(SOC_MCASP_1_FIFO_REGS, 1, 1);

    /* Set I2S format in the transmitter/receiver format units */
    McASPTxFmtI2SSet(SOC_MCASP_1_CTRL_REGS, WORD_SIZE, SLOT_SIZE,
                     MCASP_TX_MODE_DMA);

    McASPTxFrameSyncCfg(SOC_MCASP_1_CTRL_REGS, 2, MCASP_TX_FS_WIDTH_WORD,
                        MCASP_TX_FS_EXT_BEGIN_ON_RIS_EDGE);

    /* configure the clock for transmitter */
    McASPTxClkCfg(SOC_MCASP_1_CTRL_REGS, MCASP_TX_CLK_EXTERNAL, 0, 0);
    McASPTxClkPolaritySet(SOC_MCASP_1_CTRL_REGS, MCASP_TX_CLK_POL_FALL_EDGE);
    McASPTxClkCheckConfig(SOC_MCASP_1_CTRL_REGS, MCASP_TX_CLKCHCK_DIV32,
                          0x00, 0xFF);

    /* Enable the transmitter/receiver slots. I2S uses 2 slots */
    McASPTxTimeSlotSet(SOC_MCASP_1_CTRL_REGS, I2S_SLOTS_L_R);

    /* Set the serializer as transmitter */
    McASPSerializerTxSet(SOC_MCASP_1_CTRL_REGS, MCASP_XSER_TX);

    /*
    ** Configure the McASP pins
    ** Input - Frame Sync, Clock and Serializer 12
    ** Output - Serializer 11 is connected to the input of the codec
    */
    McASPPinMcASPSet(SOC_MCASP_1_CTRL_REGS, 0xFFFFFFFF);
    McASPPinDirOutputSet(SOC_MCASP_1_CTRL_REGS, MCASP_PIN_AXR(MCASP_XSER_TX));
    McASPPinDirInputSet(SOC_MCASP_1_CTRL_REGS, MCASP_PIN_AFSX
                                               | MCASP_PIN_ACLKX);

}

/*
** Sets up the interrupts for EDMA in AINTC
*/
static void EDMA3IntSetup(void)
{
    IntRegister(SYS_INT_EDMACOMPINT, EDMA3CCComplIsr);

    IntPrioritySet(SYS_INT_EDMACOMPINT, 0, AINTC_HOSTINT_ROUTE_IRQ);

    /* Enabling the system interrupt in AINTC. */
    IntSystemEnable(SYS_INT_EDMACOMPINT);
}

/*
** Activates the data transmission/reception
** The DMA parameters shall be ready before calling this function.
*/
static void I2SDataTxRxActivate(void)
{
    /* Start the clocks */
    McASPTxClkStart(SOC_MCASP_1_CTRL_REGS, MCASP_TX_CLK_EXTERNAL);

    /* Enable EDMA for the transfer */
    EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS,
                        EDMA3_CHA_MCASP1_TX, EDMA3_TRIG_MODE_EVENT);

    /* Activate the  serializers */
    McASPTxSerActivate(SOC_MCASP_1_CTRL_REGS);

    /* make sure that the XDATA bit is cleared to zero */
    while(McASPTxStatusGet(SOC_MCASP_1_CTRL_REGS) & MCASP_TX_STAT_DATAREADY);

    /* Activate the state machines */
    McASPTxEnable(SOC_MCASP_1_CTRL_REGS);
}

/*
** Activates the DMA transfer for a parameterset from the given buffer.
*/
void BufferTxDMAActivate(unsigned int txBuf, unsigned short numSamples,
                         unsigned short parId, unsigned short linkPar)
{
    EDMA3CCPaRAMEntry paramSet;

    /* Copy the default paramset */
    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET);

    /* Enable completion interrupt */
    paramSet.opt |= TX_DMA_INT_ENABLE;
    paramSet.srcAddr =  txBufPtr[txBuf];
    paramSet.linkAddr = linkPar * SIZE_PARAMSET;
    paramSet.bCnt = numSamples;

    EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, parId, &paramSet);
}

/* Initializes the sound module */
void SoundInit(void)
{
    /* Set up pin mux for I2C module 1 and McASP module 1 */
    I2C0ModuleClkConfig();
    I2CPinMuxSetup(0);

    McASP1ModuleClkConfig();
    McASP1PinMuxSetup();

    EDMAModuleClkConfig();

    /* Initialize the I2C 1 interface for the codec AIC31 */
    I2CCodecIfInit(SOC_I2C_0_REGS, I2C_SLAVE_CODEC_AIC31);

    EDMA3Init(SOC_EDMA30CC_0_REGS, 0);
    EDMA3IntSetup();

    /*
    ** Request EDMA channels.
    */
    EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
                        EDMA3_CHA_MCASP1_TX, EDMA3_CHA_MCASP1_TX, 0);

    /* Initialize the DMA parameters */
    I2SDMAParamInit();

    /* Configure the Codec for I2S mode */
    AIC31I2SConfigure();

    /* Configure the McASP for I2S */
    McASPI2SConfigure();

    /* Activate the audio transmission and reception */
    I2SDataTxRxActivate();
}

/*
** When called, plays a tone
*/
void SoundClickPlay(unsigned char* toneBase, unsigned int toneSize)
{
    unsigned short parToSend;
    unsigned short parToLink;
    unsigned int toneOffset;
    unsigned int len;

    toneOffset = 0;

    while(toneOffset < toneSize)
    {
        parToSend =  PAR_TX_START + parOffTxToSend;
        parOffTxToSend = (parOffTxToSend + 1) % NUM_PAR;
        parToLink  = PAR_TX_START + parOffTxToSend;

        lastSentTxBuf = (lastSentTxBuf + 1) % NUM_BUF;

        len = toneSize - toneOffset;

        if(len > AUDIO_BUF_SIZE)
        {
            len = AUDIO_BUF_SIZE;
        }

        /* Copy the buffer */
        memcpy((void *)txBufPtr[lastSentTxBuf],
               (void *)(toneBase + toneOffset),
               len);

        toneOffset += len;

        /*
        ** Send the buffer by setting the DMA params accordingly.
        */
        BufferTxDMAActivate(lastSentTxBuf,
                            (unsigned short)(len / ((WORD_SIZE >> 3) * NUM_I2S_CHANNELS)),
                            (unsigned short)parToSend,
                            (unsigned short)parToLink);
    }
}

/*
** This function will be called once transmit DMA is completed
*/
static void McASPTxDMAComplHandler(void)
{
    parOffSent = (parOffSent + 1) % NUM_PAR;
    ParamTxLoopJobSet((unsigned short)(PAR_TX_START + parOffSent));
}

/*
** EDMA transfer completion ISR
*/
static void EDMA3CCComplIsr(void)
{
    /* Check if transmit DMA completed */
    if(EDMA3GetIntrStatus(SOC_EDMA30CC_0_REGS) & (1 << EDMA3_CHA_MCASP1_TX))
    {
        /* Clear the interrupt status for the first channel */
        EDMA3ClrIntr(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP1_TX);
        McASPTxDMAComplHandler();
    }
}

/***************************** End Of File ***********************************/


text:
package com.ibm.icu.dev.test.util;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.impl.CurrencyData;
import com.ibm.icu.text.CurrencyDisplayNames;
import com.ibm.icu.text.CurrencyMetaInfo;
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyFilter;
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyInfo;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;

/**
 * @test
 * @summary General test of Currency
 */
public class CurrencyTest extends TestFmwk {

    public static void main(String[] args) throws Exception {
        new CurrencyTest().run(args);
    }

    /**
     * Test of basic API.
     */
    public void TestAPI() {
        Currency usd = Currency.getInstance("USD");
        /*int hash = */usd.hashCode();
        Currency jpy = Currency.getInstance("JPY");
        if (usd.equals(jpy)) {
            errln("FAIL: USD == JPY");
        }
        if (usd.equals("abc")) {
            errln("FAIL: USD == (String)");
        }
        if (usd.equals(null)) {
            errln("FAIL: USD == (null)");
        }
        if (!usd.equals(usd)) {
            errln("FAIL: USD != USD");
        }

        try {
            Currency nullCurrency = Currency.getInstance((String)null);
            errln("FAIL: Expected getInstance(null) to throw "
                    + "a NullPointerException, but returned " + nullCurrency);
        } catch (NullPointerException npe) {
            logln("PASS: getInstance(null) threw a NullPointerException");
        }

        try {
            Currency bogusCurrency = Currency.getInstance("BOGUS");
            errln("FAIL: Expected getInstance(\"BOGUS\") to throw "
                    + "an IllegalArgumentException, but returned " + bogusCurrency);
        } catch (IllegalArgumentException iae) {
            logln("PASS: getInstance(\"BOGUS\") threw an IllegalArgumentException");
        }

        Locale[] avail = Currency.getAvailableLocales();
        if(avail==null){
            errln("FAIL: getAvailableLocales returned null");
        }

        try {
            usd.getName(ULocale.US, 5, new boolean[1]);
            errln("expected getName with invalid type parameter to throw exception");
        }
        catch (Exception e) {
            logln("PASS: getName failed as expected");
        }
    }

    /**
     * Test registration.
     */
    public void TestRegistration() {
        final Currency jpy = Currency.getInstance("JPY");
        final Currency usd = Currency.getInstance(Locale.US);

    try {
      Currency.unregister(null); // should fail, coverage
      errln("expected unregister of null to throw exception");
    }
    catch (Exception e) {
        logln("PASS: unregister of null failed as expected");
    }

    if (Currency.unregister("")) { // coverage
      errln("unregister before register erroneously succeeded");
    }

        ULocale fu_FU = new ULocale("fu_FU");

        Object key1 = Currency.registerInstance(jpy, ULocale.US);
        Object key2 = Currency.registerInstance(jpy, fu_FU);

        Currency nus = Currency.getInstance(Locale.US);
        if (!nus.equals(jpy)) {
            errln("expected " + jpy + " but got: " + nus);
        }

        // converage, make sure default factory works
        Currency nus1 = Currency.getInstance(Locale.JAPAN);
        if (!nus1.equals(jpy)) {
            errln("expected " + jpy + " but got: " + nus1);
        }

        ULocale[] locales = Currency.getAvailableULocales();
        boolean found = false;
        for (int i = 0; i < locales.length; ++i) {
            if (locales[i].equals(fu_FU)) {
                found = true;
                break;
            }
        }
        if (!found) {
            errln("did not find locale" + fu_FU + " in currency locales");
        }

        if (!Currency.unregister(key1)) {
            errln("unable to unregister currency using key1");
        }
        if (!Currency.unregister(key2)) {
            errln("unable to unregister currency using key2");
        }

        Currency nus2 = Currency.getInstance(Locale.US);
        if (!nus2.equals(usd)) {
            errln("expected " + usd + " but got: " + nus2);
        }

        locales = Currency.getAvailableULocales();
        found = false;
        for (int i = 0; i < locales.length; ++i) {
            if (locales[i].equals(fu_FU)) {
                found = true;
                break;
            }
        }
        if (found) {
            errln("found locale" + fu_FU + " in currency locales after unregister");
        }

        Locale[] locs = Currency.getAvailableLocales();
        found = false;
        for (int i = 0; i < locs.length; ++i) {
            if (locs[i].equals(fu_FU)) {
                found = true;
                break;
            }
        }
        if (found) {
            errln("found locale" + fu_FU + " in currency locales after unregister");
        }
    }

    /**
     * Test names.
     */
    public void TestNames() {
        // Do a basic check of getName()
        // USD { "US$", "US Dollar"            } // 04/04/1792-
        ULocale en = ULocale.ENGLISH;
        boolean[] isChoiceFormat = new boolean[1];
        Currency usd = Currency.getInstance("USD");
        // Warning: HARD-CODED LOCALE DATA in this test.  If it fails, CHECK
        // THE LOCALE DATA before diving into the code.
        if (!noData()) {
            assertEquals("USD.getName(SYMBOL_NAME)",
                         "$",
                         usd.getName(en, Currency.SYMBOL_NAME, isChoiceFormat));
            assertEquals("USD.getName(LONG_NAME)",
                         "US Dollar",
                         usd.getName(en, Currency.LONG_NAME, isChoiceFormat));
        }
        // TODO add more tests later
    }

    public void TestCoverage() {
        Currency usd = Currency.getInstance("USD");
        if (!noData()) {
        assertEquals("USD.getSymbol()",
                "$",
                usd.getSymbol());
        }
    }

    // A real test of the CurrencyDisplayNames class.
    public void TestCurrencyDisplayNames() {
        if (!CurrencyDisplayNames.hasData()) {
            errln("hasData() should return true.");
        }

        // with substitute
        CurrencyDisplayNames cdn = CurrencyDisplayNames.getInstance(ULocale.GERMANY);
        assertEquals("de_USD_name", "US-Dollar", cdn.getName("USD"));
        assertEquals("de_USD_symbol", "$", cdn.getSymbol("USD"));
        assertEquals("de_USD_plural_other", "US-Dollar", cdn.getPluralName("USD", "other"));
        // unknown plural category, substitute "other"
        assertEquals("de_USD_plural_foo", "US-Dollar", cdn.getPluralName("USD", "foo"));

        cdn = CurrencyDisplayNames.getInstance(ULocale.forLanguageTag("en-US"));
        assertEquals("en-US_USD_name", "US Dollar", cdn.getName("USD"));
        assertEquals("en-US_USD_symbol", "$", cdn.getSymbol("USD"));
        assertEquals("en-US_USD_plural_one", "US dollar", cdn.getPluralName("USD", "one"));
        assertEquals("en-US_USD_plural_other", "US dollars", cdn.getPluralName("USD", "other"));

        assertEquals("en-US_FOO_name", "FOO", cdn.getName("FOO"));
        assertEquals("en-US_FOO_symbol", "FOO", cdn.getSymbol("FOO"));
        assertEquals("en-US_FOO_plural_other", "FOO", cdn.getPluralName("FOO", "other"));

        assertEquals("en-US bundle", "en", cdn.getULocale().toString());

        cdn = CurrencyDisplayNames.getInstance(ULocale.forLanguageTag("zz-Gggg-YY"));
        assertEquals("bundle from current locale", "en", cdn.getULocale().toString());

        // with no substitute
        cdn = CurrencyDisplayNames.getInstance(ULocale.GERMANY, true);
        assertNotNull("have currency data for Germany", cdn);

        // known currency, behavior unchanged
        assertEquals("de_USD_name", "US-Dollar", cdn.getName("USD"));
        assertEquals("de_USD_symbol", "$", cdn.getSymbol("USD"));
        assertEquals("de_USD_plural_other", "US-Dollar", cdn.getPluralName("USD", "other"));

        // known currency but unknown plural category
        assertNull("de_USD_plural_foo", cdn.getPluralName("USD", "foo"));

        // unknown currency, get null
        assertNull("de_FOO_name", cdn.getName("FOO"));
        assertNull("de_FOO_symbol", cdn.getSymbol("FOO"));
        assertNull("de_FOO_plural_other", cdn.getPluralName("FOO", "other"));
        assertNull("de_FOO_plural_foo", cdn.getPluralName("FOO", "foo"));

        // unknown locale with no substitute
        cdn = CurrencyDisplayNames.getInstance(ULocale.forLanguageTag("zz-Gggg-YY"), true);
        String ln = "";
        if (cdn != null) {
            ln = " (" + cdn.getULocale().toString() + ")";
        }
        assertNull("no fallback from unknown locale" + ln , cdn);
    }

    // Coverage-only test of CurrencyData
    public void TestCurrencyData() {
        CurrencyData.DefaultInfo info_fallback = (CurrencyData.DefaultInfo)CurrencyData.DefaultInfo.getWithFallback(true);
        if (info_fallback == null) {
            errln("getWithFallback() returned null.");
            return;
        }

        CurrencyData.DefaultInfo info_nofallback = (CurrencyData.DefaultInfo)CurrencyData.DefaultInfo.getWithFallback(false);
        if (info_nofallback == null) {
            errln("getWithFallback() returned null.");
            return;
        }

        if (!info_fallback.getName("isoCode").equals("isoCode") || info_nofallback.getName("isoCode") != null) {
            errln("Error calling getName().");
            return;
        }

        if (!info_fallback.getPluralName("isoCode", "type").equals("isoCode") || info_nofallback.getPluralName("isoCode", "type") != null) {
            errln("Error calling getPluralName().");
            return;
        }

        if (!info_fallback.getSymbol("isoCode").equals("isoCode") || info_nofallback.getSymbol("isoCode") != null) {
            errln("Error calling getSymbol().");
            return;
        }

        if (!info_fallback.symbolMap().isEmpty()) {
            errln("symbolMap() should return empty map.");
            return;
        }

        if (!info_fallback.nameMap().isEmpty()) {
            errln("nameMap() should return empty map.");
            return;
        }

        if (!info_fallback.getUnitPatterns().isEmpty() || info_nofallback.getUnitPatterns() != null) {
            errln("Error calling getUnitPatterns().");
            return;
        }

        if (!info_fallback.getSpacingInfo().equals((CurrencyData.CurrencySpacingInfo.DEFAULT)) ||
                info_nofallback.getSpacingInfo() != null) {
            errln("Error calling getSpacingInfo().");
            return;
        }

        if (info_fallback.getULocale() != ULocale.ROOT) {
            errln("Error calling getLocale().");
            return;
        }

        if (info_fallback.getFormatInfo("isoCode") != null) {
            errln("Error calling getFormatInfo().");
            return;
        }
    }

    // A real test of CurrencyMetaInfo.
    public void testCurrencyMetaInfoRanges() {
        CurrencyMetaInfo metainfo = CurrencyMetaInfo.getInstance(true);
        assertNotNull("have metainfo", metainfo);

        CurrencyFilter filter = CurrencyFilter.onRegion("DE"); // must be capitalized
        List<CurrencyInfo> currenciesInGermany = metainfo.currencyInfo(filter);
        logln("currencies: " + currenciesInGermany.size());
        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date demLastDate = new Date(Long.MAX_VALUE);
        Date eurFirstDate = new Date(Long.MIN_VALUE);
        for (CurrencyInfo info : currenciesInGermany) {
            logln(info.toString());
            logln("from: " + fmt.format(info.from)+ Long.toHexString(info.from));
            logln("  to: " + fmt.format(info.to) + Long.toHexString(info.to));
            if (info.code.equals("DEM")) {
                demLastDate = new Date(info.to);
            } else if (info.code.equals("EUR")) {
                eurFirstDate = new Date(info.from);
            }
        }

        // the Euro and Deutschmark overlapped for several years
        assertEquals("DEM available at last date", 2, metainfo.currencyInfo(filter.withDate(demLastDate)).size());

        // demLastDate + 1 millisecond is not the start of the last day, we consider it the next day, so...
        Date demLastDatePlus1ms = new Date(demLastDate.getTime() + 1);
        assertEquals("DEM not available after very start of last date", 1, metainfo.currencyInfo(filter.withDate(demLastDatePlus1ms)).size());

        // both available for start of euro
        assertEquals("EUR available on start of first date", 2, metainfo.currencyInfo(filter.withDate(eurFirstDate)).size());

        // but not one millisecond before the start of the first day
        Date eurFirstDateMinus1ms = new Date(eurFirstDate.getTime() - 1);
        assertEquals("EUR not avilable before very start of first date", 1, metainfo.currencyInfo(filter.withDate(eurFirstDateMinus1ms)).size());

        // end time is last millisecond of day
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
        cal.setTime(demLastDate);
        assertEquals("hour is 23", 23, cal.get(GregorianCalendar.HOUR_OF_DAY));
        assertEquals("minute is 59", 59, cal.get(GregorianCalendar.MINUTE));
        assertEquals("second is 59", 59, cal.get(GregorianCalendar.SECOND));
        assertEquals("millisecond is 999", 999, cal.get(GregorianCalendar.MILLISECOND));

        // start time is first millisecond of day
        cal.setTime(eurFirstDate);
        assertEquals("hour is 0", 0, cal.get(GregorianCalendar.HOUR_OF_DAY));
        assertEquals("minute is 0", 0, cal.get(GregorianCalendar.MINUTE));
        assertEquals("second is 0", 0, cal.get(GregorianCalendar.SECOND));
        assertEquals("millisecond is 0", 0, cal.get(GregorianCalendar.MILLISECOND));
    }

    public void testCurrencyMetaInfoRangesWithLongs() {
        CurrencyMetaInfo metainfo = CurrencyMetaInfo.getInstance(true);
        assertNotNull("have metainfo", metainfo);

        CurrencyFilter filter = CurrencyFilter.onRegion("DE"); // must be capitalized
        List<CurrencyInfo> currenciesInGermany = metainfo.currencyInfo(filter);
        logln("currencies: " + currenciesInGermany.size());
        long demLastDate = Long.MAX_VALUE;
        long eurFirstDate = Long.MIN_VALUE;
        for (CurrencyInfo info : currenciesInGermany) {
            logln(info.toString());
            if (info.code.equals("DEM")) {
                demLastDate = info.to;
            } else if (info.code.equals("EUR")) {
                eurFirstDate = info.from;
            }
        }
        // the Euro and Deutschmark overlapped for several years
        assertEquals("DEM available at last date", 2, metainfo.currencyInfo(filter.withDate(demLastDate)).size());

        // demLastDate + 1 millisecond is not the start of the last day, we consider it the next day, so...
        long demLastDatePlus1ms = demLastDate + 1;
        assertEquals("DEM not available after very start of last date", 1, metainfo.currencyInfo(filter.withDate(demLastDatePlus1ms)).size());

        // both available for start of euro
        assertEquals("EUR available on start of first date", 2, metainfo.currencyInfo(filter.withDate(eurFirstDate)).size());

        // but not one millisecond before the start of the first day
        long eurFirstDateMinus1ms = eurFirstDate - 1;
        assertEquals("EUR not avilable before very start of first date", 1, metainfo.currencyInfo(filter.withDate(eurFirstDateMinus1ms)).size());
    }

    public void TestWithTender() {
        CurrencyMetaInfo metainfo = CurrencyMetaInfo.getInstance();
        if (metainfo == null) {
            errln("Unable to get CurrencyMetaInfo instance.");
            return;
        }
        CurrencyMetaInfo.CurrencyFilter filter =
                CurrencyMetaInfo.CurrencyFilter.onRegion("CH");
        List<String> currencies = metainfo.currencies(filter);
        assertTrue("More than one currency for switzerland", currencies.size() > 1);
        assertEquals(
                "With tender",
                Arrays.asList(new String[] {"CHF"}),
                metainfo.currencies(filter.withTender()));
    }

    // Coverage-only test of the CurrencyMetaInfo class
    public void TestCurrencyMetaInfo() {
        CurrencyMetaInfo metainfo = CurrencyMetaInfo.getInstance();
        if (metainfo == null) {
            errln("Unable to get CurrencyMetaInfo instance.");
            return;
        }

        if (!CurrencyMetaInfo.hasData()) {
            errln("hasData() should note return false.");
            return;
        }

        CurrencyMetaInfo.CurrencyFilter filter;
        CurrencyMetaInfo.CurrencyInfo info;
        CurrencyMetaInfo.CurrencyDigits digits;

        { // CurrencyFilter
            filter = CurrencyMetaInfo.CurrencyFilter.onCurrency("currency");
            CurrencyMetaInfo.CurrencyFilter filter2 = CurrencyMetaInfo.CurrencyFilter.onCurrency("test");
            if (filter == null) {
                errln("Unable to create CurrencyFilter.");
                return;
            }

            if (filter.equals(new Object())) {
                errln("filter should not equal to Object");
                return;
            }

            if (filter.equals(filter2)) {
                errln("filter should not equal filter2");
                return;
            }

            if (filter.hashCode() == 0) {
                errln("Error getting filter hashcode");
                return;
            }

            if (filter.toString() == null) {
                errln("Error calling toString()");
                return;
            }
        }

        { // CurrencyInfo
            info = new CurrencyMetaInfo.CurrencyInfo("region", "code", 0, 1, 1, false);
            if (info == null) {
                errln("Error creating CurrencyInfo.");
                return;
            }

            if (info.toString() == null) {
                errln("Error calling toString()");
                return;
            }
        }

        { // CurrencyDigits
            digits = metainfo.currencyDigits("isoCode");
            if (digits == null) {
                errln("Unable to get CurrencyDigits.");
                return;
            }

            if (digits.toString() == null) {
                errln("Error calling toString()");
                return;
            }
        }
    }

    public void TestCurrencyKeyword() {
        ULocale locale = new ULocale("th_TH@collation=traditional;currency=QQQ");
        Currency currency = Currency.getInstance(locale);
        String result = currency.getCurrencyCode();
        if (!"QQQ".equals(result)) {
            errln("got unexpected currency: " + result);
        }
    }

    public void TestAvailableCurrencyCodes() {
        String[][] tests = {
            { "eo_AM", "1950-01-05" },
            { "eo_AM", "1969-12-31", "SUR" },
            { "eo_AM", "1991-12-26", "RUR" },
            { "eo_AM", "2000-12-23", "AMD" },
            { "eo_AD", "2000-12-23", "EUR", "ESP", "FRF", "ADP" },
            { "eo_AD", "1969-12-31", "ESP", "FRF", "ADP" },
            { "eo_AD", "1950-01-05", "ESP", "ADP" },
            { "eo_AD", "1900-01-17", "ESP" },
            { "eo_UA", "1994-12-25" },
            { "eo_QQ", "1969-12-31" },
            { "eo_AO", "2000-12-23", "AOA" },
            { "eo_AO", "1995-12-25", "AOR", "AON" },
            { "eo_AO", "1990-12-26", "AON", "AOK" },
            { "eo_AO", "1979-12-29", "AOK" },
            { "eo_AO", "1969-12-31" },
            { "eo_DE@currency=DEM", "2000-12-23", "EUR", "DEM" },
            { "eo-DE-u-cu-dem", "2000-12-23", "EUR", "DEM" },
            { "en_US", null, "USD" },
            { "en_US_PREEURO", null, "USD" },
            { "en_US_Q", null, "USD" },
        };

        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
        for (String[] test : tests) {
            ULocale locale = new ULocale(test[0]);
            String timeString = test[1];
            Date date;
            if (timeString == null) {
                date = new Date();
                timeString = "today";
            } else {
                try {
                    date = fmt.parse(timeString);
                } catch (Exception e) {
                    fail("could not parse date: " + timeString);
                    continue;
                }
            }
            String[] expected = null;
            if (test.length > 2) {
                expected = new String[test.length - 2];
                System.arraycopy(test, 2, expected, 0, expected.length);
            }
            String[] actual = Currency.getAvailableCurrencyCodes(locale, date);

            // Order is not important as of 4.4.  We never documented that it was.
            Set<String> expectedSet = new HashSet<String>();
            if (expected != null) {
                expectedSet.addAll(Arrays.asList(expected));
            }
            Set<String> actualSet = new HashSet<String>();
            if (actual != null) {
                actualSet.addAll(Arrays.asList(actual));
            }
            assertEquals(locale + " on " + timeString, expectedSet, actualSet);
        }
    }

    public void TestDeprecatedCurrencyFormat() {
        // bug 5952
        Locale locale = new Locale("sr", "QQ");
        DecimalFormatSymbols icuSymbols = new
        com.ibm.icu.text.DecimalFormatSymbols(locale);
        String symbol = icuSymbols.getCurrencySymbol();
        Currency currency = icuSymbols.getCurrency();
        String expectCur = null;
        String expectSym = "\u00A4";
        if(!symbol.toString().equals(expectSym) || currency != null) {
            errln("for " + locale + " expected " + expectSym+"/"+expectCur + " but got " + symbol+"/"+currency);
        } else {
            logln("for " + locale + " expected " + expectSym+"/"+expectCur + " and got " + symbol+"/"+currency);
        }
    }

    public void TestGetKeywordValues(){

        final String[][] PREFERRED = {
            {"root",                 },
            {"und",                  },
            {"und_ZZ",               },
            {"en_US",           "USD"},
            {"en_029",               },
            {"en_TH",           "THB"},
            {"de",              "EUR"},
            {"de_DE",           "EUR"},
            {"de_ZZ",                },
            {"ar",              "EGP"},
            {"ar_PS",           "ILS", "JOD"},
            {"en@currency=CAD",     "USD"},
            {"fr@currency=ZZZ",     "EUR"},
            {"de_DE@currency=DEM",  "EUR"},
        };

        String[] ALL = Currency.getKeywordValuesForLocale("currency", ULocale.getDefault(), false);
        HashSet ALLSET = new HashSet();
        for (int i = 0; i < ALL.length; i++) {
            ALLSET.add(ALL[i]);
        }

        for (int i = 0; i < PREFERRED.length; i++) {
            ULocale loc = new ULocale(PREFERRED[i][0]);
            String[] expected = new String[PREFERRED[i].length - 1];
            System.arraycopy(PREFERRED[i], 1, expected, 0, expected.length);
            String[] pref = Currency.getKeywordValuesForLocale("currency", loc, true);
            assertEquals(loc.toString(), expected, pref);

            String[] all = Currency.getKeywordValuesForLocale("currency", loc, false);
            // The items in the two collections should match (ignore order,
            // behavior change from 4.3.3)
            Set<String> returnedSet = new HashSet<String>();
            returnedSet.addAll(Arrays.asList(all));
            assertEquals(loc.toString(), ALLSET, returnedSet);
        }
    }

    public void TestIsAvailable() {
        Date d1995 = new Date(788918400000L);   // 1995-01-01 00:00 GMT
        Date d2000 = new Date(946684800000L);   // 2000-01-01 00:00 GMT
        Date d2005 = new Date(1104537600000L);  // 2005-01-01 00:00 GMT

        assertTrue("USD all time", Currency.isAvailable("USD", null, null));
        assertTrue("USD before 1995", Currency.isAvailable("USD", null, d1995));
        assertTrue("USD 1995-2005", Currency.isAvailable("USD", d1995, d2005));
        assertTrue("USD after 2005", Currency.isAvailable("USD", d2005, null));
        assertTrue("USD on 2005-01-01", Currency.isAvailable("USD", d2005, d2005));

        assertTrue("usd all time", Currency.isAvailable("usd", null, null));

        assertTrue("DEM all time", Currency.isAvailable("DEM", null, null));
        assertTrue("DEM before 1995", Currency.isAvailable("DEM", null, d1995));
        assertTrue("DEM 1995-2000", Currency.isAvailable("DEM", d1995, d2000));
        assertTrue("DEM 1995-2005", Currency.isAvailable("DEM", d1995, d2005));
        assertFalse("DEM after 2005", Currency.isAvailable("DEM", d2005, null));
        assertTrue("DEM on 2000-01-01", Currency.isAvailable("DEM", d2000, d2000));
        assertFalse("DEM on 2005-01-01", Currency.isAvailable("DEM", d2005, d2005));
        assertTrue("CHE all the time", Currency.isAvailable("CHE", null, null));

        assertFalse("XXY unknown code", Currency.isAvailable("XXY", null, null));

        assertFalse("USDOLLAR invalid code", Currency.isAvailable("USDOLLAR", null, null));

        // illegal argument combination
        try {
            Currency.isAvailable("USD", d2005, d1995);
            errln("Expected IllegalArgumentException, because lower range is after upper range");
        } catch (IllegalArgumentException e) {
            logln("IllegalArgumentException, because lower range is after upper range");
        }
    }

    /**
     * Test case for getAvailableCurrencies()
     */
    public void TestGetAvailableCurrencies() {
        Set<Currency> avail1 = Currency.getAvailableCurrencies();

        // returned set must be modifiable - add one more currency
        avail1.add(Currency.getInstance("ZZZ"));    // ZZZ is not defined by ISO 4217

        Set<Currency> avail2 = Currency.getAvailableCurrencies();
        assertTrue("avail1 does not contain all currencies in avail2", avail1.containsAll(avail2));
        assertTrue("avail1 must have one more currency", (avail1.size() - avail2.size() == 1));
    }

    /**
     * Test case for getNumericCode()
     */
    public void TestGetNumericCode() {
        final Object[][] NUMCODE_TESTDATA = {
            {"USD", 840},
            {"Usd", 840},   /* mixed casing */
            {"EUR", 978},
            {"JPY", 392},
            {"XFU", 0},     /* XFU: no numeric code */
            {"ZZZ", 0},     /* ZZZ: undefined ISO currency code */
        };

        for (Object[] data : NUMCODE_TESTDATA) {
            Currency cur = Currency.getInstance((String)data[0]);
            int numCode = cur.getNumericCode();
            int expected = ((Integer)data[1]).intValue();
            if (numCode != expected) {
                errln("FAIL: getNumericCode returned " + numCode + " for "
                        + cur.getCurrencyCode() + " - expected: " + expected);
            }
        }
    }

    /**
     * Test case for getDisplayName()
     */
    public void TestGetDisplayName() {
        final String[][] DISPNAME_TESTDATA = {
            {"USD", "US Dollar"},
            {"EUR", "Euro"},
            {"JPY", "Japanese Yen"},
        };

        Locale defLocale = Locale.getDefault();
        Locale jaJP = new Locale("ja", "JP");
        Locale root = new Locale("");

        for (String[] data : DISPNAME_TESTDATA) {
            Currency cur = Currency.getInstance(data[0]);
            assertEquals("getDisplayName() for " + data[0], data[1], cur.getDisplayName());
            assertEquals("getDisplayName() for " + data[0] + " in locale " + defLocale, data[1], cur.getDisplayName(defLocale));

            // ICU has localized display name for ja
            assertNotEquals("getDisplayName() for " + data[0] + " in locale " + jaJP, data[1], cur.getDisplayName(jaJP));

            // root locale does not have any localized display names,
            // so the currency code itself should be returned
            assertEquals("getDisplayName() for " + data[0] + " in locale " + root, data[0], cur.getDisplayName(root));
        }
    }

    public void TestCurrencyInfoCtor() {
        new CurrencyMetaInfo.CurrencyInfo("region", "code", 0, 0, 1);
    }
}

redpajama_set_name: RedPajamaGithub


text:
require 'byebug/command'
require 'byebug/helpers/parse'

module Byebug
  #
  # Implements the finish functionality.
  #
  # Allows the user to continue execution until certain frames are finished.
  #
  class FinishCommand < Command
    include Helpers::ParseHelper

    self.allow_in_post_mortem = false

    def self.regexp
      /^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x
    end

    def self.description
      <<-EOD
        fin[ish][ n_frames]

        #{short_description}

        If no number is given, we run until the current frame returns. If a
        number of frames `n_frames` is given, then we run until `n_frames`
        return from the current position.
      EOD
    end

    def self.short_description
      'Runs the program until frame returns'
    end

    def execute
      if @match[1]
        n_frames, err = get_int(@match[1], 'finish', 0, max_frames - 1)
        return errmsg(err) unless n_frames
      else
        n_frames = 1
      end

      force = n_frames == 0 ? true : false
      context.step_out(context.frame.pos + n_frames, force)
      context.frame = 0
      processor.proceed!
    end

    private

    def max_frames
      context.stack_size - context.frame.pos
    end
  end
end


text:
#region copyright
// <copyright file="Asset.cs" >
// Copyright (c) 2016 All Rights Reserved
// Licensed under MIT
// </copyright>
// <author>Raj Bandi</author>
// <date>26/6/2016</date>
// <summary></summary>
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using NBitcoin.Protocol;

namespace RiseSharp.Core.Common
{

    public abstract class Asset : IAsset
    {
        public abstract byte[] GetBytes();
    }
}


text:
package com.amazonaws.services.lambda.model;

import java.io.Serializable;
import com.amazonaws.AmazonWebServiceRequest;

/**
 *
 */
public class InvokeAsyncRequest extends AmazonWebServiceRequest implements
        Serializable, Cloneable {

    /**
     *

     * The Lambda function name.
     *

     */
    private String functionName;
    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     */
    private java.io.InputStream invokeArgs;

    /**
     *

     * The Lambda function name.
     *

     *
     * @param functionName
     *        The Lambda function name.
     */

    public void setFunctionName(String functionName) {
        this.functionName = functionName;
    }

    /**
     *

     * The Lambda function name.
     *

     *
     * @return The Lambda function name.
     */

    public String getFunctionName() {
        return this.functionName;
    }

    /**
     *

     * The Lambda function name.
     *

     *
     * @param functionName
     *        The Lambda function name.
     * @return Returns a reference to this object so that method calls can be
     *         chained together.
     */

    public InvokeAsyncRequest withFunctionName(String functionName) {
        setFunctionName(functionName);
        return this;
    }

    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     *
     * @param invokeArgs
     *        JSON that you want to provide to your Lambda function as input.
     */

    public void setInvokeArgs(java.io.InputStream invokeArgs) {
        this.invokeArgs = invokeArgs;
    }

    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     *
     * @return JSON that you want to provide to your Lambda function as input.
     */

    public java.io.InputStream getInvokeArgs() {
        return this.invokeArgs;
    }

    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     *
     * @param invokeArgs
     *        JSON that you want to provide to your Lambda function as input.
     * @return Returns a reference to this object so that method calls can be
     *         chained together.
     */

    public InvokeAsyncRequest withInvokeArgs(java.io.InputStream invokeArgs) {
        setInvokeArgs(invokeArgs);
        return this;
    }

    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     *
     * @param invokeArgs
     *        JSON that you want to provide to your Lambda function as input.
     */
    public void setInvokeArgs(String invokeArgs) {
        setInvokeArgs(new com.amazonaws.adapters.types.StringToInputStreamAdapter()
                .adapt(invokeArgs));
    }

    /**
     *

     * JSON that you want to provide to your Lambda function as input.
     *

     *
     * @param invokeArgs
     *        JSON that you want to provide to your Lambda function as input.
     * @return Returns a reference to this object so that method calls can be
     *         chained together.
     */
    public InvokeAsyncRequest withInvokeArgs(String invokeArgs) {
        setInvokeArgs(new com.amazonaws.adapters.types.StringToInputStreamAdapter()
                .adapt(invokeArgs));
        return this;
    }

    /**
     * Returns a string representation of this object; useful for testing and
     * debugging.
     *
     * @return A string representation of this object.
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        if (getFunctionName() != null)
            sb.append("FunctionName: " + getFunctionName() + ",");
        if (getInvokeArgs() != null)
            sb.append("InvokeArgs: " + getInvokeArgs());
        sb.append("}");
        return sb.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;

        if (obj instanceof InvokeAsyncRequest == false)
            return false;
        InvokeAsyncRequest other = (InvokeAsyncRequest) obj;
        if (other.getFunctionName() == null ^ this.getFunctionName() == null)
            return false;
        if (other.getFunctionName() != null
                && other.getFunctionName().equals(this.getFunctionName()) == false)
            return false;
        if (other.getInvokeArgs() == null ^ this.getInvokeArgs() == null)
            return false;
        if (other.getInvokeArgs() != null
                && other.getInvokeArgs().equals(this.getInvokeArgs()) == false)
            return false;
        return true;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int hashCode = 1;

        hashCode = prime
                * hashCode
                + ((getFunctionName() == null) ? 0 : getFunctionName()
                        .hashCode());
        hashCode = prime * hashCode
                + ((getInvokeArgs() == null) ? 0 : getInvokeArgs().hashCode());
        return hashCode;
    }

    @Override
    public InvokeAsyncRequest clone() {
        return (InvokeAsyncRequest) super.clone();
    }
}


text:
/**#@+
* Boolean rules bitfield
*/
const RULE_AUTO_CLOSE        = 1 << 0;
const RULE_AUTO_REOPEN       = 1 << 1;
const RULE_BREAK_PARAGRAPH   = 1 << 2;
const RULE_CREATE_PARAGRAPHS = 1 << 3;
const RULE_DISABLE_AUTO_BR   = 1 << 4;
const RULE_ENABLE_AUTO_BR    = 1 << 5;
const RULE_IGNORE_TAGS       = 1 << 6;
const RULE_IGNORE_TEXT       = 1 << 7;
const RULE_IGNORE_WHITESPACE = 1 << 8;
const RULE_IS_TRANSPARENT    = 1 << 9;
const RULE_PREVENT_BR        = 1 << 10;
const RULE_SUSPEND_AUTO_BR   = 1 << 11;
const RULE_TRIM_FIRST_LINE   = 1 << 12;
/**#@-*/

/**
* @const Bitwise disjunction of rules related to automatic line breaks
*/
const RULES_AUTO_LINEBREAKS = RULE_DISABLE_AUTO_BR | RULE_ENABLE_AUTO_BR | RULE_SUSPEND_AUTO_BR;

/**
* @const Bitwise disjunction of rules that are inherited by subcontexts
*/
const RULES_INHERITANCE = RULE_ENABLE_AUTO_BR;

/**
* @const All the characters that are considered whitespace
*/
const WHITESPACE = " \n\t";

/**
* @type {!Object.<string,number>} Number of open tags for each tag name
*/
let cntOpen;

/**
* @type {!Object.<string,number>} Number of times each tag has been used
*/
let cntTotal;

/**
* @type {!Object} Current context
*/
let context;

/**
* @type {number} How hard the parser has worked on fixing bad markup so far
*/
let currentFixingCost;

/**
* @type {?Tag} Current tag being processed
*/
let currentTag;

/**
* @type {boolean} Whether the output contains "rich" tags, IOW any tag that is not
 or

*/
let isRich;

/**
* @type {!Logger} This parser's logger
*/
let logger = new Logger;

/**
* @type {number} How hard the parser should work on fixing bad markup
*/
let maxFixingCost = 10000;

/**
* @type {!Object} Associative array of namespace prefixes in use in document (prefixes used as key)
*/
let namespaces;

/**
* @type {!Array.<!Tag>} Stack of open tags (instances of Tag)
*/
let openTags;

/**
* @type {string} This parser's output
*/
let output;

/**
* @type {!Object.<!Object>}
*/
const plugins;

/**
* @type {number} Position of the cursor in the original text
*/
let pos;

/**
* @type {!Object} Variables registered for use in filters
*/
const registeredVars;

/**
* @type {!Object} Root context, used at the root of the document
*/
const rootContext;

/**
* @type {!Object} Tags' config
*/
const tagsConfig;

/**
* @type {!Array.<!Tag>} Tag storage
*/
let tagStack;

/**
* @type {boolean} Whether the tags in the stack are sorted
*/
let tagStackIsSorted;

/**
* @type {string} Text being parsed
*/
let text;

/**
* @type {number} Length of the text being parsed
*/
let textLen;

/**
* @type {number} Counter incremented everytime the parser is reset. Used to as a canary to detect
*                 whether the parser was reset during execution
*/
let uid = 0;

/**
* @type {number} Position before which we output text verbatim, without paragraphs or linebreaks
*/
let wsPos;

//==========================================================================
// Public API
//==========================================================================

/**
* Disable a tag
*
* @param {string} tagName Name of the tag
*/
function disableTag(tagName)
{
	if (tagsConfig[tagName])
	{
		copyTagConfig(tagName).isDisabled = true;
	}
}

/**
* Enable a tag
*
* @param {string} tagName Name of the tag
*/
function enableTag(tagName)
{
	if (tagsConfig[tagName])
	{
		copyTagConfig(tagName).isDisabled = false;
	}
}

/**
* Get this parser's Logger instance
*
* @return {!Logger}
*/
function getLogger()
{
	return logger;
}

/**
* Parse a text
*
* @param  {string} _text Text to parse
* @return {string}       XML representation
*/
function parse(_text)
{
	// Reset the parser and save the uid
	reset(_text);
	let _uid = uid;

	// Do the heavy lifting
	executePluginParsers();
	processTags();

	// Finalize the document
	finalizeOutput();

	// Check the uid in case a plugin or a filter reset the parser mid-execution
	if (uid !== _uid)
	{
		throw 'The parser has been reset during execution';
	}

	// Log a warning if the fixing cost limit was exceeded
	if (currentFixingCost > maxFixingCost)
	{
		logger.warn('Fixing cost limit exceeded');
	}

	return output;
}

/**
* Reset the parser for a new parsing
*
* @param {string} _text Text to be parsed
*/
function reset(_text)
{
	// Normalize CR/CRLF to LF, remove control characters that aren't allowed in XML
	_text = _text.replace(/\r\n?/g, "\n");
	_text = _text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]+/g, '');

	// Clear the logs
	logger.clear();

	// Initialize the rest
	cntOpen           = {};
	cntTotal          = {};
	currentFixingCost = 0;
	currentTag        = null;
	isRich            = false;
	namespaces        = {};
	openTags          = [];
	output            = '';
	pos               = 0;
	tagStack          = [];
	tagStackIsSorted  = false;
	text              = _text;
	textLen           = text.length;
	wsPos             = 0;

	// Initialize the root context
	context = rootContext;
	context.inParagraph = false;

	// Bump the UID
	++uid;
}

/**
* Change a tag's tagLimit
*
* NOTE: the default tagLimit should generally be set during configuration instead
*
* @param {string} tagName  The tag's name, in UPPERCASE
* @param {number} tagLimit
*/
function setTagLimit(tagName, tagLimit)
{
	if (tagsConfig[tagName])
	{
		copyTagConfig(tagName).tagLimit = tagLimit;
	}
}

/**
* Change a tag's nestingLimit
*
* NOTE: the default nestingLimit should generally be set during configuration instead
*
* @param {string} tagName      The tag's name, in UPPERCASE
* @param {number} nestingLimit
*/
function setNestingLimit(tagName, nestingLimit)
{
	if (tagsConfig[tagName])
	{
		copyTagConfig(tagName).nestingLimit = nestingLimit;
	}
}

/**
* Copy a tag's config
*
* This method ensures that the tag's config is its own object and not shared with another
* identical tag
*
* @param  {string} tagName Tag's name
* @return {!Object}         Tag's config
*/
function copyTagConfig(tagName)
{
	let tagConfig = {}, k;
	for (k in tagsConfig[tagName])
	{
		tagConfig[k] = tagsConfig[tagName][k];
	}

	return tagsConfig[tagName] = tagConfig;
}

//==========================================================================
// Output handling
//==========================================================================

/**
* Replace Unicode characters outside the BMP with XML entities in the output
*/
function encodeUnicodeSupplementaryCharacters()
{
	output = output.replace(
		/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
		encodeUnicodeSupplementaryCharactersCallback
	);
}

/**
* Encode given surrogate pair into an XML entity
*
* @param  {string} pair Surrogate pair
* @return {string}      XML entity
*/
function encodeUnicodeSupplementaryCharactersCallback(pair)
{
	let cp = (pair.charCodeAt(0) << 10) + pair.charCodeAt(1) - 56613888;

	return '&#' + cp + ';';
}

/**
* Finalize the output by appending the rest of the unprocessed text and create the root node
*/
function finalizeOutput()
{
	let tmp;

	// Output the rest of the text and close the last paragraph
	outputText(textLen, 0, true);

	// Remove empty tag pairs, e.g.  as well as empty paragraphs
	do
	{
		tmp = output;
		output = output.replace(/<([^ />]+)[^>]*><\/\1>/g, '');
	}
	while (output !== tmp);

	// Merge consecutive  tags
	output = output.replace(/<\/i>/g, '');

	// Remove control characters from the output to ensure it's valid XML
	output = output.replace(/[\x00-\x08\x0B-\x1F]/g, '');

	// Encode Unicode characters that are outside of the BMP
	encodeUnicodeSupplementaryCharacters();

	// Use a <r> root if the text is rich, or <t> for plain text (including

 and
)
	let tagName = (isRich) ? 'r' : 't';

	// Prepare the root node with all the namespace declarations
	tmp = '<' + tagName;
	if (HINT.namespaces)
	{
		for (let prefix in namespaces)
		{
			tmp += ' xmlns:' + prefix + '="urn:s9e:TextFormatter:' + prefix + '"';
		}
	}

	output = tmp + '>' + output + '</' + tagName + '>';
}

/**
* Append a tag to the output
*
* @param {!Tag} tag Tag to append
*/
function outputTag(tag)
{
	isRich = true;

	let tagName    = tag.getName(),
		tagPos     = tag.getPos(),
		tagLen     = tag.getLen(),
		tagFlags   = tag.getFlags(),
		skipBefore = 0,
		skipAfter  = 0;

	if (HINT.RULE_IGNORE_WHITESPACE && (tagFlags & RULE_IGNORE_WHITESPACE))
	{
		skipBefore = 1;
		skipAfter  = (tag.isEndTag()) ? 2 : 1;
	}

	// Current paragraph must end before the tag if:
	//  - the tag is a start (or self-closing) tag and it breaks paragraphs, or
	//  - the tag is an end tag (but not self-closing)
	let closeParagraph = !!(!tag.isStartTag() || (HINT.RULE_BREAK_PARAGRAPH && (tagFlags & RULE_BREAK_PARAGRAPH)));

	// Let the cursor catch up with this tag's position
	outputText(tagPos, skipBefore, closeParagraph);

	// Capture the text consumed by the tag
	let tagText = (tagLen)
				? htmlspecialchars_noquotes(text.substring(tagPos, tagPos + tagLen))
				: '';

	// Output current tag
	if (tag.isStartTag())
	{
		// Handle paragraphs before opening the tag
		if (!HINT.RULE_BREAK_PARAGRAPH || !(tagFlags & RULE_BREAK_PARAGRAPH))
		{
			outputParagraphStart(tagPos);
		}

		// Record this tag's namespace, if applicable
		if (HINT.namespaces)
		{
			let colonPos = tagName.indexOf(':');
			if (colonPos > 0)
			{
				namespaces[tagName.substring(0, colonPos)] = 0;
			}
		}

		// Open the start tag and add its attributes, but don't close the tag
		output += '<' + tagName;

		// We output the attributes in lexical order. Helps canonicalizing the output and could
		// prove useful someday
		let attributes = tag.getAttributes(),
			attributeNames = [];
		for (let attrName in attributes)
		{
			attributeNames.push(attrName);
		}
		attributeNames.sort((a, b) => (a > b) ? 1 : -1);
		attributeNames.forEach(
			(attrName) =>
			{
				output += ' ' + attrName + '="' + htmlspecialchars_compat(attributes[attrName].toString()).replace(/\n/g, '
') + '"';
			}
		);

		if (tag.isSelfClosingTag())
		{
			if (tagLen)
			{
				output += '>' + tagText + '</' + tagName + '>';
			}
			else
			{
				output += '/>';
			}
		}
		else if (tagLen)
		{
			output += '><s>' + tagText + '</s>';
		}
		else
		{
			output += '>';
		}
	}
	else
	{
		if (tagLen)
		{
			output += '<e>' + tagText + '</e>';
		}

		output += '</' + tagName + '>';
	}

	// Move the cursor past the tag
	pos = tagPos + tagLen;

	// Skip newlines (no other whitespace) after this tag
	wsPos = pos;
	while (skipAfter && wsPos < textLen && text[wsPos] === "\n")
	{
		// Decrement the number of lines to skip
		--skipAfter;

		// Move the cursor past the newline
		++wsPos;
	}
}

/**
* Output the text between the cursor's position (included) and given position (not included)
*
* @param  {number}  catchupPos     Position we're catching up to
* @param  {number}  maxLines       Maximum number of lines to ignore at the end of the text
* @param  {boolean} closeParagraph Whether to close the paragraph at the end, if applicable
*/
function outputText(catchupPos, maxLines, closeParagraph)
{
	if (closeParagraph)
	{
		if (!(context.flags & RULE_CREATE_PARAGRAPHS))
		{
			closeParagraph = false;
		}
		else
		{
			// Ignore any number of lines at the end if we're closing a paragraph
			maxLines = -1;
		}
	}

	if (pos >= catchupPos)
	{
		// We're already there, close the paragraph if applicable and return
		if (closeParagraph)
		{
			outputParagraphEnd();
		}
	}

	// Skip over previously identified whitespace if applicable
	if (wsPos > pos)
	{
		let skipPos = Math.min(catchupPos, wsPos);
		output += text.substring(pos, skipPos);
		pos = skipPos;

		if (pos >= catchupPos)
		{
			// Skipped everything. Close the paragraph if applicable and return
			if (closeParagraph)
			{
				outputParagraphEnd();
			}
		}
	}

	let catchupText;

	// Test whether we're even supposed to output anything
	if (HINT.RULE_IGNORE_TEXT && context.flags & RULE_IGNORE_TEXT)
	{
		catchupText = text.substring(pos, catchupPos);

		// If the catchup text is not entirely composed of whitespace, we put it inside ignore tags
		if (!/^[ \n\t]*$/.test(catchupText))
		{
			catchupText = '' + htmlspecialchars_noquotes(catchupText) + '';
		}

		output += catchupText;
		pos = catchupPos;

		if (closeParagraph)
		{
			outputParagraphEnd();
		}

		return;
	}

	// Compute the amount of text to ignore at the end of the output
	let ignorePos = catchupPos,
		ignoreLen = 0;

	// Ignore as many lines (including whitespace) as specified
	while (maxLines && --ignorePos >= pos)
	{
		let c = text[ignorePos];
		if (c !== ' ' && c !== "\n" && c !== "\t")
		{
			break;
		}

		if (c === "\n")
		{
			--maxLines;
		}

		++ignoreLen;
	}

	// Adjust catchupPos to ignore the text at the end
	catchupPos -= ignoreLen;

	// Break down the text in paragraphs if applicable
	if (HINT.RULE_CREATE_PARAGRAPHS && context.flags & RULE_CREATE_PARAGRAPHS)
	{
		if (!context.inParagraph)
		{
			outputWhitespace(catchupPos);

			if (catchupPos > pos)
			{
				outputParagraphStart(catchupPos);
			}
		}

		// Look for a paragraph break in this text
		let pbPos = text.indexOf("\n\n", pos);

		while (pbPos > -1 && pbPos < catchupPos)
		{
			outputText(pbPos, 0, true);
			outputParagraphStart(catchupPos);

			pbPos = text.indexOf("\n\n", pos);
		}
	}

	// Capture, escape and output the text
	if (catchupPos > pos)
	{
		catchupText = htmlspecialchars_noquotes(
			text.substring(pos, catchupPos)
		);

		// Format line breaks if applicable
		if (HINT.RULE_ENABLE_AUTO_BR && (context.flags & RULES_AUTO_LINEBREAKS) === RULE_ENABLE_AUTO_BR)
		{
			catchupText = catchupText.replace(/\n/g, "
\n");
		}

		output += catchupText;
	}

	// Close the paragraph if applicable
	if (closeParagraph)
	{
		outputParagraphEnd();
	}

	// Add the ignored text if applicable
	if (ignoreLen)
	{
		output += text.substring(catchupPos, catchupPos + ignoreLen);
	}

	// Move the cursor past the text
	pos = catchupPos + ignoreLen;
}

/**
* Output a linebreak tag
*
* @param {!Tag} tag
*/
function outputBrTag(tag)
{
	outputText(tag.getPos(), 0, false);
	output += '
';
}

/**
* Output an ignore tag
*
* @param {!Tag} tag
*/
function outputIgnoreTag(tag)
{
	let tagPos = tag.getPos(),
		tagLen = tag.getLen();

	// Capture the text to ignore
	let ignoreText = text.substring(tagPos, tagPos + tagLen);

	// Catch up with the tag's position then output the tag
	outputText(tagPos, 0, false);
	output += '' + htmlspecialchars_noquotes(ignoreText) + '';
	isRich = true;

	// Move the cursor past this tag
	pos = tagPos + tagLen;
}

/**
* Start a paragraph between current position and given position, if applicable
*
* @param  {number} maxPos Rightmost position at which the paragraph can be opened
*/
function outputParagraphStart(maxPos)
{
	if (!HINT.RULE_CREATE_PARAGRAPHS)
	{
		return;
	}

	// Do nothing if we're already in a paragraph, or if we don't use paragraphs
	if (context.inParagraph
	 || !(context.flags & RULE_CREATE_PARAGRAPHS))
	{
		return;
	}

	// Output the whitespace between pos and maxPos if applicable
	outputWhitespace(maxPos);

	// Open the paragraph, but only if it's not at the very end of the text
	if (pos < textLen)
	{
		output += '
';
		context.inParagraph = true;
	}
}

/**
* Close current paragraph at current position if applicable
*/
function outputParagraphEnd()
{
	// Do nothing if we're not in a paragraph
	if (!context.inParagraph)
	{
		return;
	}

	output += '
';
	context.inParagraph = false;
}

/**
* Output the content of a verbatim tag
*
* @param {!Tag} tag
*/
function outputVerbatim(tag)
{
	let flags = context.flags;
	context.flags = tag.getFlags();
	outputText(currentTag.getPos() + currentTag.getLen(), 0, false);
	context.flags = flags;
}

/**
* Skip as much whitespace after current position as possible
*
* @param  {number} maxPos Rightmost character to be skipped
*/
function outputWhitespace(maxPos)
{
	while (pos < maxPos && " \n\t".indexOf(text[pos]) > -1)
	{
		output += text[pos];
		++pos;
	}
}

//==========================================================================
// Plugins handling
//==========================================================================

/**
* Disable a plugin
*
* @param {string} pluginName Name of the plugin
*/
function disablePlugin(pluginName)
{
	if (plugins[pluginName])
	{
		plugins[pluginName].isDisabled = true;
	}
}

/**
* Enable a plugin
*
* @param {string} pluginName Name of the plugin
*/
function enablePlugin(pluginName)
{
	if (plugins[pluginName])
	{
		plugins[pluginName].isDisabled = false;
	}
}

/**
* Execute given plugin
*
* @param {string} pluginName Plugin's name
*/
function executePluginParser(pluginName)
{
	let pluginConfig = plugins[pluginName];
	if (pluginConfig.quickMatch && text.indexOf(pluginConfig.quickMatch) < 0)
	{
		return;
	}

	let matches = [];
	if (HINT.regexp && HINT.regexpLimit && typeof pluginConfig.regexp !== 'undefined' && typeof pluginConfig.regexpLimit !== 'undefined')
	{
		matches = getMatches(pluginConfig.regexp, pluginConfig.regexpLimit);
		if (!matches.length)
		{
			return;
		}
	}

	// Execute the plugin's parser, which will add tags via addStartTag() and others
	getPluginParser(pluginName)(text, matches);
}

/**
* Execute all the plugins
*/
function executePluginParsers()
{
	for (let pluginName in plugins)
	{
		if (!plugins[pluginName].isDisabled)
		{
			executePluginParser(pluginName);
		}
	}
}

/**
* Get regexp matches in a manner similar to preg_match_all() with PREG_SET_ORDER | PREG_OFFSET_CAPTURE
*
* @param  {!RegExp} regexp
* @param  {number}  limit
* @return {!Array.<!Array>}
*/
function getMatches(regexp, limit)
{
	// Reset the regexp
	regexp.lastIndex = 0;
	let matches = [], cnt = 0, m;
	while (++cnt <= limit && (m = regexp.exec(text)))
	{
		// NOTE: coercing m.index to a number because Closure Compiler thinks pos is a string otherwise
		let pos   = m.index,
			match = [[m[0], pos]],
			i = 0;
		while (++i < m.length)
		{
			let str = m[i];

			// Sub-expressions that were not evaluated return undefined
			if (str === undefined)
			{
				match.push(['', -1]);
			}
			else
			{
				match.push([str, text.indexOf(str, pos)]);
				pos += str.length;
			}
		}

		matches.push(match);
	}

	return matches;
}

/**
* Get the callback for given plugin's parser
*
* @param  {string} pluginName
* @return {function(string, !Array)}
*/
function getPluginParser(pluginName)
{
	return plugins[pluginName].parser;
}

/**
* Register a parser
*
* Can be used to add a new parser with no plugin config, or pre-generate a parser for an
* existing plugin
*
* @param  {string}    pluginName
* @param  {!Function} parser
* @param  {?RegExp=}  regexp
* @param  {number=}   limit
*/
function registerParser(pluginName, parser, regexp, limit)
{
	// Create an empty config for this plugin to ensure it is executed
	if (!plugins[pluginName])
	{
		plugins[pluginName] = {};
	}
	if (regexp)
	{
		plugins[pluginName].regexp = regexp;
		plugins[pluginName].limit  = limit || Infinity;
	}
	plugins[pluginName].parser = parser;
}

//==========================================================================
// Rules handling
//==========================================================================

/**
* Apply closeAncestor rules associated with given tag
*
* @param  {!Tag}    tag Tag
* @return {boolean}     Whether a new tag has been added
*/
function closeAncestor(tag)
{
	if (!HINT.closeAncestor)
	{
		return false;
	}

	if (openTags.length)
	{
		let tagName   = tag.getName(),
			tagConfig = tagsConfig[tagName];

		if (tagConfig.rules.closeAncestor)
		{
			let i = openTags.length;

			while (--i >= 0)
			{
				let ancestor     = openTags[i],
					ancestorName = ancestor.getName();

				if (tagConfig.rules.closeAncestor[ancestorName])
				{
					++currentFixingCost;

					// We have to close this ancestor. First we reinsert this tag...
					tagStack.push(tag);

					// ...then we add a new end tag for it with a better priority
					addMagicEndTag(ancestor, tag.getPos(), tag.getSortPriority() - 1);

					return true;
				}
			}
		}
	}

	return false;
}

/**
* Apply closeParent rules associated with given tag
*
* @param  {!Tag}    tag Tag
* @return {boolean}     Whether a new tag has been added
*/
function closeParent(tag)
{
	if (!HINT.closeParent)
	{
		return false;
	}

	if (openTags.length)
	{
		let tagName   = tag.getName(),
			tagConfig = tagsConfig[tagName];

		if (tagConfig.rules.closeParent)
		{
			let parent     = openTags[openTags.length - 1],
				parentName = parent.getName();

			if (tagConfig.rules.closeParent[parentName])
			{
				++currentFixingCost;

				// We have to close that parent. First we reinsert the tag...
				tagStack.push(tag);

				// ...then we add a new end tag for it with a better priority
				addMagicEndTag(parent, tag.getPos(), tag.getSortPriority() - 1);

				return true;
			}
		}
	}

	return false;
}

/**
* Apply the createChild rules associated with given tag
*
* @param {!Tag} tag Tag
*/
function createChild(tag)
{
	if (!HINT.createChild)
	{
		return;
	}

	let tagConfig = tagsConfig[tag.getName()];
	if (tagConfig.rules.createChild)
	{
		let priority = -1000,
			_text    = text.substring(pos),
			tagPos   = pos + _text.length - _text.replace(/^[ \n\r\t]+/, '').length;
		tagConfig.rules.createChild.forEach((tagName) =>
		{
			addStartTag(tagName, tagPos, 0, ++priority);
		});
	}
}

/**
* Apply fosterParent rules associated with given tag
*
* NOTE: this rule has the potential for creating an unbounded loop, either if a tag tries to
*       foster itself or two or more tags try to foster each other in a loop. We mitigate the
*       risk by preventing a tag from creating a child of itself (the parent still gets closed)
*       and by checking and increasing the currentFixingCost so that a loop of multiple tags
*       do not run indefinitely. The default tagLimit and nestingLimit also serve to prevent the
*       loop from running indefinitely
*
* @param  {!Tag}    tag Tag
* @return {boolean}     Whether a new tag has been added
*/
function fosterParent(tag)
{
	if (!HINT.fosterParent)
	{
		return false;
	}

	if (openTags.length)
	{
		let tagName   = tag.getName(),
			tagConfig = tagsConfig[tagName];

		if (tagConfig.rules.fosterParent)
		{
			let parent     = openTags[openTags.length - 1],
				parentName = parent.getName();

			if (tagConfig.rules.fosterParent[parentName])
			{
				if (parentName !== tagName && currentFixingCost < maxFixingCost)
				{
					addFosterTag(tag, parent);
				}

				// Reinsert current tag
				tagStack.push(tag);

				// And finally close its parent with a priority that ensures it is processed
				// before this tag
				addMagicEndTag(parent, tag.getPos(), tag.getSortPriority() - 1);

				// Adjust the fixing cost to account for the additional tags/processing
				currentFixingCost += 4;

				return true;
			}
		}
	}

	return false;
}

/**
* Apply requireAncestor rules associated with given tag
*
* @param  {!Tag}    tag Tag
* @return {boolean}     Whether this tag has an unfulfilled requireAncestor requirement
*/
function requireAncestor(tag)
{
	if (!HINT.requireAncestor)
	{
		return false;
	}

	let tagName   = tag.getName(),
		tagConfig = tagsConfig[tagName];

	if (tagConfig.rules.requireAncestor)
	{
		let i = tagConfig.rules.requireAncestor.length;
		while (--i >= 0)
		{
			let ancestorName = tagConfig.rules.requireAncestor[i];
			if (cntOpen[ancestorName])
			{
				return false;
			}
		}

		logger.err('Tag requires an ancestor', {
			'requireAncestor' : tagConfig.rules.requireAncestor.join(', '),
			'tag'             : tag
		});

		return true;
	}

	return false;
}

//==========================================================================
// Tag processing
//==========================================================================

/**
* Create and add a copy of a tag as a child of a given tag
*
* @param {!Tag} tag       Current tag
* @param {!Tag} fosterTag Tag to foster
*/
function addFosterTag(tag, fosterTag)
{
	let coords    = getMagicStartCoords(tag.getPos() + tag.getLen()),
		childPos  = coords[0],
		childPrio = coords[1];

	// Add a 0-width copy of the parent tag after this tag and make it depend on this tag
	let childTag = addCopyTag(fosterTag, childPos, 0, childPrio);
	tag.cascadeInvalidationTo(childTag);
}

/**
* Create and add an end tag for given start tag at given position
*
* @param  {!Tag}    startTag Start tag
* @param  {number}  tagPos   End tag's position (will be adjusted for whitespace if applicable)
* @param  {number=} prio     End tag's priority
* @return {!Tag}
*/
function addMagicEndTag(startTag, tagPos, prio)
{
	let tagName = startTag.getName();

	// Adjust the end tag's position if whitespace is to be minimized
	if (HINT.RULE_IGNORE_WHITESPACE && ((currentTag.getFlags() | startTag.getFlags()) & RULE_IGNORE_WHITESPACE))
	{
		tagPos = getMagicEndPos(tagPos);
	}

	// Add a 0-width end tag that is paired with the given start tag
	let endTag = addEndTag(tagName, tagPos, 0, prio || 0);
	endTag.pairWith(startTag);

	return endTag;
}

/**
* Compute the position of a magic end tag, adjusted for whitespace
*
* @param  {number} tagPos Rightmost possible position for the tag
* @return {number}
*/
function getMagicEndPos(tagPos)
{
	// Back up from given position to the cursor's position until we find a character that
	// is not whitespace
	while (tagPos > pos && WHITESPACE.indexOf(text[tagPos - 1]) > -1)
	{
		--tagPos;
	}

	return tagPos;
}

/**
* Compute the position and priority of a magic start tag, adjusted for whitespace
*
* @param  {number} tagPos Leftmost possible position for the tag
* @return {!Array}        [Tag pos, priority]
*/
function getMagicStartCoords(tagPos)
{
	let nextPos, nextPrio, nextTag, prio;
	if (!tagStack.length)
	{
		// Set the next position outside the text boundaries
		nextPos  = textLen + 1;
		nextPrio = 0;
	}
	else
	{
		nextTag  = tagStack[tagStack.length - 1];
		nextPos  = nextTag.getPos();
		nextPrio = nextTag.getSortPriority();
	}

	// Find the first non-whitespace position before next tag or the end of text
	while (tagPos < nextPos && WHITESPACE.indexOf(text[tagPos]) > -1)
	{
		++tagPos;
	}

	// Set a priority that ensures this tag appears before the next tag
	prio = (tagPos === nextPos) ? nextPrio - 1 : 0;

	return [tagPos, prio];
}

/**
* Test whether given start tag is immediately followed by a closing tag
*
* @param  {!Tag} tag Start tag (including self-closing)
* @return {boolean}
*/
function isFollowedByClosingTag(tag)
{
	return (!tagStack.length) ? false : tagStack[tagStack.length - 1].canClose(tag);
}

/**
* Process all tags in the stack
*/
function processTags()
{
	if (!tagStack.length)
	{
		return;
	}

	// Initialize the count tables
	for (let tagName in tagsConfig)
	{
		cntOpen[tagName]  = 0;
		cntTotal[tagName] = 0;
	}

	// Process the tag stack, close tags that were left open and repeat until done
	do
	{
		while (tagStack.length)
		{
			if (!tagStackIsSorted)
			{
				sortTags();
			}

			currentTag = tagStack.pop();
			processCurrentTag();
		}

		// Close tags that were left open
		openTags.forEach((startTag) =>
		{
			// NOTE: we add tags in hierarchical order (ancestors to descendants) but since
			//       the stack is processed in LIFO order, it means that tags get closed in
			//       the correct order, from descendants to ancestors
			addMagicEndTag(startTag, textLen);
		});
	}
	while (tagStack.length);
}

/**
* Process current tag
*/
function processCurrentTag()
{
	// Invalidate current tag if tags are disabled and current tag would not close the last open
	// tag and is not a system tag
	if ((context.flags & RULE_IGNORE_TAGS)
	 && !currentTag.canClose(openTags[openTags.length - 1])
	 && !currentTag.isSystemTag())
	{
		currentTag.invalidate();
	}

	let tagPos = currentTag.getPos(),
		tagLen = currentTag.getLen();

	// Test whether the cursor passed this tag's position already
	if (pos > tagPos && !currentTag.isInvalid())
	{
		// Test whether this tag is paired with a start tag and this tag is still open
		let startTag = currentTag.getStartTag();

		if (startTag && openTags.indexOf(startTag) >= 0)
		{
			// Create an end tag that matches current tag's start tag, which consumes as much of
			// the same text as current tag and is paired with the same start tag
			addEndTag(
				startTag.getName(),
				pos,
				Math.max(0, tagPos + tagLen - pos)
			).pairWith(startTag);

			// Note that current tag is not invalidated, it's merely replaced
			return;
		}

		// If this is an ignore tag, try to ignore as much as the remaining text as possible
		if (currentTag.isIgnoreTag())
		{
			let ignoreLen = tagPos + tagLen - pos;

			if (ignoreLen > 0)
			{
				// Create a new ignore tag and move on
				addIgnoreTag(pos, ignoreLen);

				return;
			}
		}

		// Skipped tags are invalidated
		currentTag.invalidate();
	}

	if (currentTag.isInvalid())
	{
		return;
	}

	if (currentTag.isIgnoreTag())
	{
		outputIgnoreTag(currentTag);
	}
	else if (currentTag.isBrTag())
	{
		// Output the tag if it's allowed, ignore it otherwise
		if (!HINT.RULE_PREVENT_BR || !(context.flags & RULE_PREVENT_BR))
		{
			outputBrTag(currentTag);
		}
	}
	else if (currentTag.isParagraphBreak())
	{
		outputText(currentTag.getPos(), 0, true);
	}
	else if (currentTag.isVerbatim())
	{
		outputVerbatim(currentTag);
	}
	else if (currentTag.isStartTag())
	{
		processStartTag(currentTag);
	}
	else
	{
		processEndTag(currentTag);
	}
}

/**
* Process given start tag (including self-closing tags) at current position
*
* @param {!Tag} tag Start tag (including self-closing)
*/
function processStartTag(tag)
{
	let tagName   = tag.getName(),
		tagConfig = tagsConfig[tagName];

	// 1. Check that this tag has not reached its global limit tagLimit
	// 2. Execute this tag's filterChain, which will filter/validate its attributes
	// 3. Apply closeParent, closeAncestor and fosterParent rules
	// 4. Check for nestingLimit
	// 5. Apply requireAncestor rules
	//
	// This order ensures that the tag is valid and within the set limits before we attempt to
	// close parents or ancestors. We need to close ancestors before we can check for nesting
	// limits, whether this tag is allowed within current context (the context may change
	// as ancestors are closed) or whether the required ancestors are still there (they might
	// have been closed by a rule.)
	if (cntTotal[tagName] >= tagConfig.tagLimit)
	{
		logger.err(
			'Tag limit exceeded',
			{
				'tag'      : tag,
				'tagName'  : tagName,
				'tagLimit' : tagConfig.tagLimit
			}
		);
		tag.invalidate();

		return;
	}

	filterTag(tag);
	if (tag.isInvalid())
	{
		return;
	}

	if (currentFixingCost < maxFixingCost)
	{
		if (fosterParent(tag) || closeParent(tag) || closeAncestor(tag))
		{
			// This tag parent/ancestor needs to be closed, we just return (the tag is still valid)
			return;
		}
	}

	if (cntOpen[tagName] >= tagConfig.nestingLimit)
	{
		logger.err(
			'Nesting limit exceeded',
			{
				'tag'          : tag,
				'tagName'      : tagName,
				'nestingLimit' : tagConfig.nestingLimit
			}
		);
		tag.invalidate();

		return;
	}

	if (!tagIsAllowed(tagName))
	{
		let msg     = 'Tag is not allowed in this context',
			context = {'tag': tag, 'tagName': tagName};
		if (tag.getLen() > 0)
		{
			logger.warn(msg, context);
		}
		else
		{
			logger.debug(msg, context);
		}
		tag.invalidate();

		return;
	}

	if (requireAncestor(tag))
	{
		tag.invalidate();

		return;
	}

	// If this tag has an autoClose rule and it's not self-closed, paired with an end tag, or
	// immediately followed by an end tag, we replace it with a self-closing tag with the same
	// properties
	if (HINT.RULE_AUTO_CLOSE
	 && tag.getFlags() & RULE_AUTO_CLOSE
	 && !tag.isSelfClosingTag()
	 && !tag.getEndTag()
	 && !isFollowedByClosingTag(tag))
	{
		let newTag = new Tag(Tag.SELF_CLOSING_TAG, tagName, tag.getPos(), tag.getLen());
		newTag.setAttributes(tag.getAttributes());
		newTag.setFlags(tag.getFlags());

		tag = newTag;
	}

	if (HINT.RULE_TRIM_FIRST_LINE
	 && tag.getFlags() & RULE_TRIM_FIRST_LINE
	 && text[tag.getPos() + tag.getLen()] === "\n")
	{
		addIgnoreTag(tag.getPos() + tag.getLen(), 1);
	}

	// This tag is valid, output it and update the context
	outputTag(tag);
	pushContext(tag);

	// Apply the createChild rules if applicable
	createChild(tag);
}

/**
* Process given end tag at current position
*
* @param {!Tag} tag End tag
*/
function processEndTag(tag)
{
	let tagName = tag.getName();

	if (!cntOpen[tagName])
	{
		// This is an end tag with no start tag
		return;
	}

	/**
	* @type {!Array.<!Tag>} List of tags need to be closed before given tag
	*/
	let closeTags = [];

	// Iterate through all open tags from last to first to find a match for our tag
	let i = openTags.length;
	while (--i >= 0)
	{
		let openTag = openTags[i];

		if (tag.canClose(openTag))
		{
			break;
		}

		closeTags.push(openTag);
		++currentFixingCost;
	}

	if (i < 0)
	{
		// Did not find a matching tag
		logger.debug('Skipping end tag with no start tag', {'tag': tag});

		return;
	}

	// Accumulate flags to determine whether whitespace should be trimmed
	let flags = tag.getFlags();
	closeTags.forEach((openTag) =>
	{
		flags |= openTag.getFlags();
	});
	let ignoreWhitespace = (HINT.RULE_IGNORE_WHITESPACE && (flags & RULE_IGNORE_WHITESPACE));

	// Only reopen tags if we haven't exceeded our "fixing" budget
	let keepReopening = HINT.RULE_AUTO_REOPEN && (currentFixingCost < maxFixingCost),
		reopenTags    = [];
	closeTags.forEach((openTag) =>
	{
		let openTagName = openTag.getName();

		// Test whether this tag should be reopened automatically
		if (keepReopening)
		{
			if (openTag.getFlags() & RULE_AUTO_REOPEN)
			{
				reopenTags.push(openTag);
			}
			else
			{
				keepReopening = false;
			}
		}

		// Find the earliest position we can close this open tag
		let tagPos = tag.getPos();
		if (ignoreWhitespace)
		{
			tagPos = getMagicEndPos(tagPos);
		}

		// Output an end tag to close this start tag, then update the context
		let endTag = new Tag(Tag.END_TAG, openTagName, tagPos, 0);
		endTag.setFlags(openTag.getFlags());
		outputTag(endTag);
		popContext();
	});

	// Output our tag, moving the cursor past it, then update the context
	outputTag(tag);
	popContext();

	// If our fixing budget allows it, peek at upcoming tags and remove end tags that would
	// close tags that are already being closed now. Also, filter our list of tags being
	// reopened by removing those that would immediately be closed
	if (closeTags.length && currentFixingCost < maxFixingCost)
	{
		/**
		* @type {number} Rightmost position of the portion of text to ignore
		*/
		let ignorePos = pos;

		i = tagStack.length;
		while (--i >= 0 && ++currentFixingCost < maxFixingCost)
		{
			let upcomingTag = tagStack[i];

			// Test whether the upcoming tag is positioned at current "ignore" position and it's
			// strictly an end tag (not a start tag or a self-closing tag)
			if (upcomingTag.getPos() > ignorePos
			 || upcomingTag.isStartTag())
			{
				break;
			}

			// Test whether this tag would close any of the tags we're about to reopen
			let j = closeTags.length;

			while (--j >= 0 && ++currentFixingCost < maxFixingCost)
			{
				if (upcomingTag.canClose(closeTags[j]))
				{
					// Remove the tag from the lists and reset the keys
					closeTags.splice(j, 1);

					if (reopenTags[j])
					{
						reopenTags.splice(j, 1);
					}

					// Extend the ignored text to cover this tag
					ignorePos = Math.max(
						ignorePos,
						upcomingTag.getPos() + upcomingTag.getLen()
					);

					break;
				}
			}
		}

		if (ignorePos > pos)
		{
			/**
			* @todo have a method that takes (pos,len) rather than a Tag
			*/
			outputIgnoreTag(new Tag(Tag.SELF_CLOSING_TAG, 'i', pos, ignorePos - pos));
		}
	}

	// Re-add tags that need to be reopened, at current cursor position
	reopenTags.forEach((startTag) =>
	{
		let newTag = addCopyTag(startTag, pos, 0);

		// Re-pair the new tag
		let endTag = startTag.getEndTag();
		if (endTag)
		{
			newTag.pairWith(endTag);
		}
	});
}

/**
* Update counters and replace current context with its parent context
*/
function popContext()
{
	let tag = openTags.pop();
	--cntOpen[tag.getName()];
	context = context.parentContext;
}

/**
* Update counters and replace current context with a new context based on given tag
*
* If given tag is a self-closing tag, the context won't change
*
* @param {!Tag} tag Start tag (including self-closing)
*/
function pushContext(tag)
{
	let tagName   = tag.getName(),
		tagFlags  = tag.getFlags(),
		tagConfig = tagsConfig[tagName];

	++cntTotal[tagName];

	// If this is a self-closing tag, the context remains the same
	if (tag.isSelfClosingTag())
	{
		return;
	}

	// Recompute the allowed tags
	let allowed = [];
	context.allowed.forEach((v, k) =>
	{
		// If the current tag is not transparent, override the low bits (allowed children) of
		// current context with its high bits (allowed descendants)
		if (!HINT.RULE_IS_TRANSPARENT || !(tagFlags & RULE_IS_TRANSPARENT))
		{
			v = (v & 0xFF00) | (v >> 8);
		}
		allowed.push(tagConfig.allowed[k] & v);
	});

	// Use this tag's flags as a base for this context and add inherited rules
	let flags = tagFlags | (context.flags & RULES_INHERITANCE);

	// RULE_DISABLE_AUTO_BR turns off RULE_ENABLE_AUTO_BR
	if (flags & RULE_DISABLE_AUTO_BR)
	{
		flags &= ~RULE_ENABLE_AUTO_BR;
	}

	++cntOpen[tagName];
	openTags.push(tag);
	context         = { parentContext : context };
	context.allowed = allowed;
	context.flags   = flags;
}

/**
* Return whether given tag is allowed in current context
*
* @param  {string}  tagName
* @return {boolean}
*/
function tagIsAllowed(tagName)
{
	let n = tagsConfig[tagName].bitNumber;

	return !!(context.allowed[n >> 3] & (1 << (n & 7)));
}

//==========================================================================
// Tag stack
//==========================================================================

/**
* Add a start tag
*
* @param  {string}  name Name of the tag
* @param  {number}  pos  Position of the tag in the text
* @param  {number}  len  Length of text consumed by the tag
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addStartTag(name, pos, len, prio)
{
	return addTag(Tag.START_TAG, name, pos, len, prio || 0);
}

/**
* Add an end tag
*
* @param  {string}  name Name of the tag
* @param  {number}  pos  Position of the tag in the text
* @param  {number}  len  Length of text consumed by the tag
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addEndTag(name, pos, len, prio)
{
	return addTag(Tag.END_TAG, name, pos, len, prio || 0);
}

/**
* Add a self-closing tag
*
* @param  {string}  name Name of the tag
* @param  {number}  pos  Position of the tag in the text
* @param  {number}  len  Length of text consumed by the tag
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addSelfClosingTag(name, pos, len, prio)
{
	return addTag(Tag.SELF_CLOSING_TAG, name, pos, len, prio || 0);
}

/**
* Add a 0-width "br" tag to force a line break at given position
*
* @param  {number}  pos  Position of the tag in the text
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addBrTag(pos, prio)
{
	return addTag(Tag.SELF_CLOSING_TAG, 'br', pos, 0, prio || 0);
}

/**
* Add an "ignore" tag
*
* @param  {number}  pos  Position of the tag in the text
* @param  {number}  len  Length of text consumed by the tag
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addIgnoreTag(pos, len, prio)
{
	return addTag(Tag.SELF_CLOSING_TAG, 'i', pos, Math.min(len, textLen - pos), prio || 0);
}

/**
* Add a paragraph break at given position
*
* Uses a zero-width tag that is actually never output in the result
*
* @param  {number}  pos  Position of the tag in the text
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addParagraphBreak(pos, prio)
{
	return addTag(Tag.SELF_CLOSING_TAG, 'pb', pos, 0, prio || 0);
}

/**
* Add a copy of given tag at given position and length
*
* @param  {!Tag}    tag  Original tag
* @param  {number}  pos  Copy's position
* @param  {number}  len  Copy's length
* @param  {number=} prio Tags' priority
* @return {!Tag}         Copy tag
*/
function addCopyTag(tag, pos, len, prio)
{
	let copy = addTag(tag.getType(), tag.getName(), pos, len, tag.getSortPriority());
	copy.setAttributes(tag.getAttributes());

	return copy;
}

/**
* Add a tag
*
* @param  {number}  type Tag's type
* @param  {string}  name Name of the tag
* @param  {number}  pos  Position of the tag in the text
* @param  {number}  len  Length of text consumed by the tag
* @param  {number=} prio Tags' priority
* @return {!Tag}
*/
function addTag(type, name, pos, len, prio)
{
	// Create the tag
	let tag = new Tag(type, name, pos, len, prio || 0);

	// Set this tag's rules bitfield
	if (tagsConfig[name])
	{
		tag.setFlags(tagsConfig[name].rules.flags);
	}

	// Invalidate this tag if it's an unknown tag, a disabled tag, if either of its length or
	// position is negative or if it's out of bounds
	if ((!tagsConfig[name] && !tag.isSystemTag()) || isInvalidTextSpan(pos, len))
	{
		tag.invalidate();
	}
	else if (tagsConfig[name] && tagsConfig[name].isDisabled)
	{
		logger.warn(
			'Tag is disabled',
			{
				'tag'     : tag,
				'tagName' : name
			}
		);
		tag.invalidate();
	}
	else
	{
		insertTag(tag);
	}

	return tag;
}

/**
* Test whether given text span is outside text boundaries or an invalid UTF sequence
*
* @param  {number}  pos Start of text
* @param  {number}  len Length of text
* @return {boolean}
*/
function isInvalidTextSpan(pos, len)
{
	return (len < 0 || pos < 0 || pos + len > textLen || /[\uDC00-\uDFFF]/.test(text.substring(pos, pos + 1) + text.substring(pos + len, pos + len + 1)));
}

/**
* Insert given tag in the tag stack
*
* @param {!Tag} tag
*/
function insertTag(tag)
{
	if (!tagStackIsSorted)
	{
		tagStack.push(tag);
	}
	else
	{
		// Scan the stack and copy every tag to the next slot until we find the correct index
		let i   = tagStack.length,
			key = getSortKey(tag);
		while (i > 0 && key > getSortKey(tagStack[i - 1]))
		{
			tagStack[i] = tagStack[i - 1];
			--i;
		}
		tagStack[i] = tag;
	}
}

/**
* Add a pair of tags
*
* @param  {string} name     Name of the tags
* @param  {number} startPos Position of the start tag
* @param  {number} startLen Length of the start tag
* @param  {number} endPos   Position of the start tag
* @param  {number} endLen   Length of the start tag
* @param  {number=}  prio     Start tag's priority (the end tag will be set to minus that value)
* @return {!Tag}             Start tag
*/
function addTagPair(name, startPos, startLen, endPos, endLen, prio)
{
	// NOTE: the end tag is added first to try to keep the stack in the correct order
	let endTag   = addEndTag(name, endPos, endLen, -prio || 0),
		startTag = addStartTag(name, startPos, startLen, prio || 0);
	startTag.pairWith(endTag);

	return startTag;
}

/**
* Add a tag that represents a verbatim copy of the original text
*
* @param  {number} pos  Position of the tag in the text
* @param  {number} len  Length of text consumed by the tag
* @param  {number=} prio Tag's priority
* @return {!Tag}
*/
function addVerbatim(pos, len, prio)
{
	return addTag(Tag.SELF_CLOSING_TAG, 'v', pos, len, prio || 0);
}

/**
* Sort tags by position and precedence
*/
function sortTags()
{
	let arr  = {},
		keys = [],
		i    = tagStack.length;
	while (--i >= 0)
	{
		let tag = tagStack[i],
			key = getSortKey(tag, i);
		keys.push(key);
		arr[key] = tag;
	}
	keys.sort();

	i = keys.length;
	tagStack = [];
	while (--i >= 0)
	{
		tagStack.push(arr[keys[i]]);
	}

	tagStackIsSorted = true;
}

/**
* Generate a key for given tag that can be used to compare its position using lexical comparisons
*
* Tags are sorted by position first, then by priority, then by whether they consume any text,
* then by length, and finally in order of their creation.
*
* The stack's array is in reverse order. Therefore, tags that appear at the start of the text
* are at the end of the array.
*
* @param  {!Tag}    tag
* @param  {number=} tagIndex
* @return {string}
*/
function getSortKey(tag, tagIndex)
{
	// Ensure that negative values are sorted correctly by flagging them and making them positive
	let prioFlag = (tag.getSortPriority() >= 0),
		prio     = tag.getSortPriority();
	if (!prioFlag)
	{
		prio += (1 << 30);
	}

	// Sort 0-width tags separately from the rest
	let lenFlag = (tag.getLen() > 0),
		lenOrder;
	if (lenFlag)
	{
		// Inverse their length so that longest matches are processed first
		lenOrder = textLen - tag.getLen();
	}
	else
	{
		// Sort self-closing tags in-between start tags and end tags to keep them outside of tag
		// pairs
		let order = {};
		order[Tag.END_TAG]          = 0;
		order[Tag.SELF_CLOSING_TAG] = 1;
		order[Tag.START_TAG]        = 2;
		lenOrder = order[tag.getType()];
	}

	return hex32(tag.getPos()) + (+prioFlag) + hex32(prio) + (+lenFlag) + hex32(lenOrder) + hex32(tagIndex || 0);
}

/**
* Format given number to a 32 bit hex value
*
* @param  {number} number
* @return {string}
*/
function hex32(number)
{
	let hex = number.toString(16);

	return "        ".substring(hex.length) + hex;
}


text:
package $package;

import co.cdev.agave.web.Destination;
import co.cdev.agave.web.Destinations;
import co.cdev.agave.Route;
import co.cdev.agave.configuration.RoutingContext;

import javax.servlet.http.HttpServletRequest;

public class WelcomeHandler {

    /**
     * Serves the same purpose as that of a 'welcome-file' entry in the web.xml file.
     * Note, that binding a handler to the root of the application like we are doing here will
     * always override the 'welcome-file' entry.
     *
     * @param handlerContext the context that this handler method executes under
     * @throws Exception if anything goes wrong
     * @return a destination object that wraps the index.jsp page
     */
    @Route("/")
    public Destination welcome(RoutingContext routingContext) throws Exception {
        HttpServletRequest request = routingContext.getRequest();

        request.setAttribute("userName", System.getProperty("user.name"));
        request.setAttribute("contextPath", request.getContextPath());

        return Destinations.forward("/WEB-INF/jsp/index.jsp");
    }

}


text: export * from './settings/settings.component';


text:
package uk.ac.ebi.atlas.model.experiment.baseline;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import uk.ac.ebi.atlas.model.experiment.Experiment;
import uk.ac.ebi.atlas.model.experiment.ExperimentDesign;
import uk.ac.ebi.atlas.model.experiment.ExperimentType;
import uk.ac.ebi.atlas.species.Species;

import java.util.Collection;
import java.util.Date;
import java.util.List;

public class SingleCellBaselineExperiment extends Experiment<Cell> {

    public SingleCellBaselineExperiment(ExperimentType type,
                                        String accession,
                                        Date lastUpdate,
                                        String displayName,
                                        String description,
                                        // String disclaimer,
                                        Species species,
                                        Collection<String> pubMedIds,
                                        Collection<String> dois,
                                        ExperimentDesign experimentDesign,
                                        // List<String> dataProviderURL,
                                        List<String> dataProviderDescription,
                                        List<String> alternativeViews,
                                        List<String> alternativeViewDescriptions,
                                        List<Cell> cells
                                        //ExperimentDisplayDefaults experimentDisplayDefaults
                                        ) {

        super(type, accession, null, lastUpdate, displayName, description, null, species,
                pubMedIds, dois, experimentDesign, null, dataProviderDescription,
                alternativeViews, alternativeViewDescriptions, cells, null);
    }

    @Override
    protected JsonObject propertiesForAssay(String runOrAssay) {
        return new JsonObject();
    }

    @Override
    public JsonArray groupingsForHeatmap() {
        return new JsonArray();
    }
}


text:
<?php
$scenario = (null !== $scenario) ? $scenario : new \StdClass();

$I = new ApiTester($scenario);

$I->wantTo('GET 401 Unauthorized: /api/v1/users');

$I->setHeader('Accept', '*/*');
$I->setHeader('Accept-Language', 'en-GB');

$I->sendGET('/api/v1/users');
$I->seeResponseCodeIs(401);
$I->seeResponseIsJson();
$I->seeResponseJsonMatchesJsonPath('$.error');