text:
package org.apache.jackrabbit.oak.plugins.index.property;

import static com.google.common.collect.ImmutableList.of;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
import static org.apache.jackrabbit.oak.api.Type.NAME;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
import static org.apache.jackrabbit.oak.spi.query.PropertyValues.newString;
import static org.junit.Assert.assertNotNull;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.RepositoryException;

import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.api.ResultRow;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.query.QueryEngineSettings;
import org.apache.jackrabbit.oak.query.ast.Operator;
import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.EditorHook;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.jackrabbit.oak.spi.query.PropertyValues;
import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.util.NodeUtil;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQueryTest {
    private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndexQueryTest.class);

    private static final EditorHook HOOK = new EditorHook(new IndexUpdateProvider(
        new OrderedPropertyIndexEditorProvider()));

    @Override
    protected void createTestIndexNode() throws Exception {
        createTestIndexNode("/");
    }

    protected void createTestIndexNode(String path) throws Exception {
        Tree index = root.getTree(path);
        IndexUtils.createIndexDefinition(new NodeUtil(index.getChild(INDEX_DEFINITIONS_NAME)),
                TEST_INDEX_NAME, false, new String[] { ORDERED_PROPERTY }, null, OrderedIndex.TYPE);
        root.commit();
    }

    /**
     * Query the index for retrieving all the entries
     *
     * @throws CommitFailedException
     * @throws ParseException
     * @throws RepositoryException
     */
    @Test
    public void queryAllEntries() throws CommitFailedException, ParseException, RepositoryException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        results = executeQuery(String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), SQL2, null)
            .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    /**
     * test the index for returning the items related to a single key
     *
     * @throws CommitFailedException
     * @throws ParseException
     */
    @Test
    public void queryOneKey() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        // getting the middle of the random list of nodes.
        ValuePathTuple searchfor = nodes.get(NUMBER_OF_NODES / 2);

        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
            PropertyValues.newString(searchfor.getValue()));
        String query = "SELECT * FROM [%s] WHERE %s=$%s";
        Iterator<? extends ResultRow> results = executeQuery(
            String.format(query, NT_UNSTRUCTURED, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter)
            .getRows().iterator();
        assertTrue("one element is expected", results.hasNext());
        assertEquals("wrong path returned", searchfor.getPath(), results.next().getPath());
        assertFalse("there should be not any more items", results.hasNext());

        setTraversalEnabled(true);
    }

    /**
     * checks the {@code OR} and {@code IN} conditions in queries.
     *
     * @throws Exception
     */
    @Test
    public void queryOrIn() throws Exception {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // generates:
        //  /test/n0, /test/n1, /test/n2
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(3), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        String statementOr = "SELECT * FROM [nt:unstructured] " +
            "WHERE " + ORDERED_PROPERTY + " = $or1 " +
            "OR " + ORDERED_PROPERTY + " = $or2";
        String statementIn = "SELECT * FROM [nt:unstructured] WHERE " + ORDERED_PROPERTY
                             + " IN($or1, $or2)";
        List<ValuePathTuple> expected = of(nodes.get(0), nodes.get(1));
        Map<String, PropertyValue> bindings = ImmutableMap.of(
            "or1", newString(expected.get(0).getValue()),
            "or2", newString(expected.get(1).getValue()));

        assertRightOrder(expected, executeQuery(statementOr, SQL2, bindings).getRows().iterator());
        assertRightOrder(expected, executeQuery(statementIn, SQL2, bindings).getRows().iterator());

        setTraversalEnabled(true);
    }

    /**
     * test the range query in case of '>' condition
     *
     * @throws CommitFailedException
     * @throws ParseException
     */
    @Test
    public void queryGreaterThan() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] AS n WHERE n.%s > $%s";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();
        List<ValuePathTuple> nodes = addChildNodes(
                generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendar = (Calendar) start.clone();
        searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
            PropertyValues.newDate(searchFor));
        Iterator<? extends ResultRow> results = executeQuery(
            String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows()
            .iterator();
        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.GreaterThanPredicate(searchFor)).iterator();
        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);
    }

    /**
     * test the range query in case of '>=' condition
     * @throws CommitFailedException
     * @throws ParseException
     */
    @Test
    public void queryGreaterEqualThan() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] AS n WHERE n.%s >= $%s";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();
        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendar = (Calendar) start.clone();
        searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
            PropertyValues.newDate(searchFor));
        Iterator<? extends ResultRow> results = executeQuery(
            String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows()
            .iterator();
        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
                new ValuePathTuple.GreaterThanPredicate(searchFor, true))
                .iterator();
        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);
    }

    /**
     * test the range query in case of '<' condition
     *
     * @throws Exception
     */
    @Test
    public void queryLessThan() throws Exception {
        initWithProperProvider();
        setTraversalEnabled(false);
        final OrderDirection direction = OrderDirection.DESC;
        final String query = "SELECT * FROM [nt:base] AS n WHERE n.%s < $%s";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();
        List<ValuePathTuple> nodes = addChildNodes(
                generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendar = (Calendar) start.clone();
        searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());

        // re-sorting ascending as the index is ASC and the results will be returned as ASC
        Collections.sort(nodes);
        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.LessThanPredicate(searchFor, false)).iterator();

        if (LOG.isDebugEnabled()) {
            LOG.debug("-- Expected results:");
            while (filtered.hasNext()) {
                LOG.debug("{}", filtered.next());
            }
            // re-initialising the filtered for the test
            filtered = Iterables.filter(nodes,
                new ValuePathTuple.LessThanPredicate(searchFor, false)).iterator();
        }

        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
                PropertyValues.newDate(searchFor));
        Iterator<? extends ResultRow> results = executeQuery(
            String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows()
            .iterator();
        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throught all the results", results.hasNext());

        setTraversalEnabled(true);
    }

    /**
     * test the range query in case of '<=' condition
     *
     * in this case as we're ascending we're expecting an empty resultset with the proper
     * provider. not the lowcost one.
     * @throws Exception
     */
    @Test
    public void queryLessEqualThan() throws Exception {
        initWithProperProvider();
        initWithProperProvider();
        setTraversalEnabled(false);
        final OrderDirection direction = OrderDirection.DESC;
        final String query = "SELECT * FROM [nt:base] AS n WHERE n.%s <= $%s";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();
        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendar = (Calendar) start.clone();
        searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());

        // re-sorting ascending as the index is ASC and the results will be returned as ASC
        Collections.sort(nodes);
        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.LessThanPredicate(searchFor, true)).iterator();

        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
            PropertyValues.newDate(searchFor));
        Iterator<? extends ResultRow> results = executeQuery(
            String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows()
            .iterator();

        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throught all the results", results.hasNext());

        setTraversalEnabled(true);
    }

    @Test
    public void queryAllEntriesWithOrderBy() throws CommitFailedException, ParseException, RepositoryException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        String query = String.format(
                "SELECT * from [nt:base] WHERE %s IS NOT NULL ORDER BY %s",
                ORDERED_PROPERTY,
                ORDERED_PROPERTY);
        results = executeQuery(query, SQL2, null)
            .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    @Test @Ignore("OAK-1763")  // FIXME OAK-1763
    public void orderByOnDouble() throws CommitFailedException, ParseException, RepositoryException {
        setTraversalEnabled(false);

        Tree test = root.getTree("/").addChild("test");

        double v1 = 1.0E18;
        double v2 = 2.0E17;
        assertTrue(v2 <= v1);  // To be super sure ;-)

        Tree child1 = test.addChild(String.valueOf(v1));
        child1.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME);
        child1.setProperty(ORDERED_PROPERTY, v1, Type.DOUBLE);

        Tree child2 = test.addChild(String.valueOf(v2));
        child2.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME);
        child2.setProperty(ORDERED_PROPERTY, v2, Type.DOUBLE);

        root.commit();

        String query = String.format(
                "SELECT * from [nt:base] WHERE %s IS NOT NULL ORDER BY %s",
                ORDERED_PROPERTY,
                ORDERED_PROPERTY);
        Iterator<? extends ResultRow> results = executeQuery(query, SQL2, null).getRows().iterator();

        assertTrue(results.hasNext());
        double r1 = Double.valueOf(PathUtils.getName(results.next().getPath()));
        assertTrue(results.hasNext());
        double r2 = Double.valueOf(PathUtils.getName(results.next().getPath()));
        assertTrue(r1 <= r2);

        setTraversalEnabled(true);
    }

    @Test
    public void orderByQueryNoWhere() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        String query = String.format(
            "SELECT * from [nt:base] ORDER BY %s",
            ORDERED_PROPERTY);
        results = executeQuery(query, SQL2, null)
            .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    @Test @Ignore("OAK-1763")  // FIXME OAK-1763
    public void orderByQueryOnSpecialChars() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<String> values = Lists.newArrayList("%", " ");
        List<ValuePathTuple> nodes = addChildNodes(values, test,
                OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        String query = String.format(
                "SELECT * from [nt:base] ORDER BY %s",
                ORDERED_PROPERTY);
        results = executeQuery(query, SQL2, null)
                .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    @Test
    public void planOderByNoWhere() throws IllegalArgumentException, RepositoryException,
                                   CommitFailedException {

        NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();

        IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
            TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
            ImmutableMap.<String, String> of());

        NodeState before = root.getNodeState();
        final OrderDirection direction = OrderDirection.ASC;
        final QueryIndex.OrderEntry.Order order = OrderDirection.ASC.equals(direction) ? QueryIndex.OrderEntry.Order.ASCENDING
                                                                                : QueryIndex.OrderEntry.Order.DESCENDING;

        List<String> values = generateOrderedValues(NUMBER_OF_NODES, direction);
        addChildNodes(values, root, Type.STRING);
        NodeState after = root.getNodeState();

        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);

        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
        final String nodeTypeName = JcrConstants.NT_BASE;

        Filter filter = createFilter(indexed, nodeTypeName);

        List<QueryIndex.OrderEntry> sortOrder = ImmutableList.of(createOrderEntry(ORDERED_PROPERTY,
            order));
        List<IndexPlan> plans = index.getPlans(filter, sortOrder, indexed);

        assertNotNull(plans);
        assertEquals(1, plans.size());
        IndexPlan p = plans.get(0);
        assertTrue(p.getEstimatedEntryCount() > 0);
        assertNotNull(p.getSortOrder());
        assertEquals(1, p.getSortOrder().size());
        QueryIndex.OrderEntry oe = p.getSortOrder().get(0);
        assertNotNull(oe);
        assertEquals(ORDERED_PROPERTY, oe.getPropertyName());
        assertEquals(QueryIndex.OrderEntry.Order.ASCENDING, oe.getOrder());
    }

    @Test
    public void queryOrderByNonIndexedProperty() throws CommitFailedException, ParseException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        String query = "SELECT * from [nt:base] ORDER BY somethingnotindexed";
        results = executeQuery(query, SQL2, null)
            .getRows().iterator();
        assertFalse("An empty resultset is expected", results.hasNext());

        setTraversalEnabled(true);
    }

    private static FilterImpl createFilter(NodeState indexed, String nodeTypeName) {
        NodeState system = indexed.getChildNode(JCR_SYSTEM);
        NodeState types = system.getChildNode(JCR_NODE_TYPES);
        NodeState type = types.getChildNode(nodeTypeName);
        SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
        return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
    }

    private static QueryIndex.OrderEntry createOrderEntry(String property,
                                                          QueryIndex.OrderEntry.Order order) {
        return new QueryIndex.OrderEntry(property, Type.UNDEFINED, order);
    }

    @Test
    public void planOrderByNonIndexedProperty() throws IllegalArgumentException,
                                               RepositoryException, CommitFailedException {
        NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();

        IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
            TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
            ImmutableMap.<String, String> of());

        NodeState before = root.getNodeState();
        final OrderDirection direction = OrderDirection.ASC;
        final QueryIndex.OrderEntry.Order order = OrderDirection.ASC.equals(direction) ? QueryIndex.OrderEntry.Order.ASCENDING
                                                                                : QueryIndex.OrderEntry.Order.DESCENDING;
        List<String> values = generateOrderedValues(NUMBER_OF_NODES, direction);
        addChildNodes(values, root, Type.STRING);
        NodeState after = root.getNodeState();

        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);

        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
        final String nodeTypeName = JcrConstants.NT_BASE;
        Filter filter = createFilter(indexed, nodeTypeName);

        List<QueryIndex.OrderEntry> sortOrder = ImmutableList.of(createOrderEntry(
            "somethingnotindexed", order));

        List<IndexPlan> plans = index.getPlans(filter, sortOrder, indexed);

        assertNotNull(plans);
        assertEquals(0, plans.size());
    }

    /**
     * tests the output of a plan where the query is asked with where conditions that are not indexed
     * but the ORDER BY are on the indexed property
     *
     * eg: SELECT * FROM [nt:base] WHERE pinned=1 ORDER BY lastModified
     *
     * @throws RepositoryException
     * @throws IllegalArgumentException
     * @throws CommitFailedException
     */
    @Test
    public void planOrderAndWhereMixed() throws IllegalArgumentException, RepositoryException, CommitFailedException {
        NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();

        IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
            TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
            ImmutableMap.<String, String> of());

        NodeState before = root.getNodeState();
        final OrderDirection direction = OrderDirection.ASC;
        final QueryIndex.OrderEntry.Order order = OrderDirection.ASC.equals(direction) ? QueryIndex.OrderEntry.Order.ASCENDING
                                                                                : QueryIndex.OrderEntry.Order.DESCENDING;
        List<String> values = generateOrderedValues(NUMBER_OF_NODES, direction);
        addChildNodes(values, root, Type.STRING);
        NodeState after = root.getNodeState();

        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);

        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
        final String nodeTypeName = JcrConstants.NT_BASE;
        FilterImpl filter = createFilter(indexed, nodeTypeName);
        filter.restrictProperty("somethingNotIndexed", Operator.EQUAL, PropertyValues.newLong(1L));

        List<QueryIndex.OrderEntry> sortOrder = ImmutableList.of(createOrderEntry(
            ORDERED_PROPERTY, order));

        List<IndexPlan> plans = index.getPlans(filter, sortOrder, indexed);
        assertNotNull(plans);
        assertEquals(1, plans.size());
        IndexPlan p = plans.get(0);
        assertTrue(p.getEstimatedEntryCount() > 0);
        assertNotNull(p.getSortOrder());
        assertEquals(1, p.getSortOrder().size());
        assertEquals(QueryIndex.OrderEntry.Order.ASCENDING, p.getSortOrder()
                .get(0).getOrder());
    }

    /**
     * query the index in case of mixed situation
     *
     * eg: SELECT * FROM [nt:base] WHERE pinned=1 ORDER BY lastModified
     *
     * @throws RepositoryException
     * @throws IllegalArgumentException
     * @throws CommitFailedException
     * @throws ParseException
     */
    @Test
    public void queryOrderAndWhereMixed() throws IllegalArgumentException, RepositoryException, CommitFailedException, ParseException {
        setTraversalEnabled(false);

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initiate the repo with some data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
            OrderDirection.ASC, Type.STRING);
        root.commit();

        String where = "wholetthedogsout";
        String value = "woof-woof-woof-woof";

        // let's set the property that will have to be queried only on 2 nodes
        Tree t = root.getTree(nodes.get(0).getPath());
        t.setProperty(where, value);
        t = root.getTree(nodes.get(1).getPath());
        t.setProperty(where, value);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        String query = String.format(
            "SELECT * from [nt:base] WHERE %s=$%s ORDER BY %s",
            where,
            where,
            ORDERED_PROPERTY
            );
        Map<String, PropertyValue> filter = ImmutableMap.of(
            where, PropertyValues.newString(value)
            );
        results = executeQuery(query, SQL2, filter)
            .getRows().iterator();
        assertTrue(results.hasNext());

        setTraversalEnabled(true);
    }

    /**
     * testing explicitly OAK-1561 use-case
     *
     * @throws CommitFailedException
     * @throws ParseException
     */
    @Test
    public void queryGreaterThenWithCast() throws CommitFailedException, ParseException {

        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY
                             + "> cast('%s' as date)";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();
        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendar = (Calendar) start.clone();
        searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
        Iterator<? extends ResultRow> results = executeQuery(String.format(query, searchFor), SQL2,
            null).getRows().iterator();
        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.GreaterThanPredicate(searchFor)).iterator();
        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);

    }

    @Test
    public void queryBetweenNoIncludes() throws Exception {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + "> $start AND "
                             + ORDERED_PROPERTY + " < $end";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();

        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendarStart = (Calendar) start.clone();
        searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
        SimpleDateFormat sdf  = new SimpleDateFormat(ISO_8601_2000);
        String searchForStart = sdf.format(searchForCalendarStart.getTime());

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
        endCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchForEnd = sdf.format(endCalendar.getTime());

        Map<String, PropertyValue> filter = ImmutableMap.of("start",
            PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
        Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows()
            .iterator();

        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, false))
            .iterator();

        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);

    }

    @Test
    public void queryBetweenIncludeLower() throws Exception {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + ">= $start AND "
                             + ORDERED_PROPERTY + " < $end";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();

        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendarStart = (Calendar) start.clone();
        searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
        String searchForStart = sdf.format(searchForCalendarStart.getTime());

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
        endCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchForEnd = sdf.format(endCalendar.getTime());

        Map<String, PropertyValue> filter = ImmutableMap.of("start",
            PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
        Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows()
            .iterator();

        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, true, false))
            .iterator();

        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);

    }

    @Test
    public void queryBetweenIncludeHigher() throws Exception {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + "> $start AND "
                             + ORDERED_PROPERTY + " <= $end";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();

        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(10, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendarStart = (Calendar) start.clone();
        searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
        String searchForStart = sdf.format(searchForCalendarStart.getTime());

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
        endCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchForEnd = sdf.format(endCalendar.getTime());

        Map<String, PropertyValue> filter = ImmutableMap.of("start",
            PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
        Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows()
            .iterator();

        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true))
            .iterator();

        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped throuhg all the results", results.hasNext());

        setTraversalEnabled(true);

    }

    @Test
    public void queryBetweenIncludeBoth() throws Exception {
        setTraversalEnabled(false);

        final OrderDirection direction = OrderDirection.ASC;
        final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + ">= $start AND "
                             + ORDERED_PROPERTY + " <= $end";

        // index automatically created by the framework:
        // {@code createTestIndexNode()}

        // initialising the data
        Tree rTree = root.getTree("/");
        Tree test = rTree.addChild("test");
        Calendar start = midnightFirstJan2013();

        List<ValuePathTuple> nodes = addChildNodes(
            generateOrderedDates(10, direction, start), test, direction, Type.DATE);
        root.commit();

        Calendar searchForCalendarStart = (Calendar) start.clone();
        searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
        String searchForStart = sdf.format(searchForCalendarStart.getTime());

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
        endCalendar.add(Calendar.HOUR_OF_DAY, -36);
        String searchForEnd = sdf.format(endCalendar.getTime());

        Map<String, PropertyValue> filter = ImmutableMap.of("start",
            PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
        Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows()
            .iterator();

        Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
            new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, true, true))
            .iterator();

        assertRightOrder(Lists.newArrayList(filtered), results);
        assertFalse("We should have looped through all the results", results.hasNext());

        setTraversalEnabled(true);

    }

    @Test
    public void indexDefinitionBelowRoot() throws Exception {
        setTraversalEnabled(false);

        // remove the default test index definition
        root.getTree("/" + INDEX_DEFINITIONS_NAME + "/" + TEST_INDEX_NAME).remove();
        root.getTree("/").addChild("test").addChild(INDEX_DEFINITIONS_NAME);
        root.commit();
        createTestIndexNode("/test");

        Tree test = root.getTree("/test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
                OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        results = executeQuery(String.format("SELECT * FROM [%s] as s WHERE s.foo IS NOT NULL and ISDESCENDANTNODE(s, '/test')", NT_UNSTRUCTURED), SQL2, null)
                .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    @Test
    public void indexDefinitionBelowRootOrderBy() throws Exception {
        setTraversalEnabled(false);

        // remove the default test index definition
        root.getTree("/" + INDEX_DEFINITIONS_NAME + "/" + TEST_INDEX_NAME).remove();
        root.getTree("/").addChild("test").addChild(INDEX_DEFINITIONS_NAME);
        root.commit();
        createTestIndexNode("/test");

        Tree test = root.getTree("/test");
        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
                OrderDirection.ASC, Type.STRING);
        root.commit();

        // querying
        Iterator<? extends ResultRow> results;
        results = executeQuery(String.format("SELECT * FROM [%s] as s WHERE ISDESCENDANTNODE(s, '/test') ORDER BY s.foo", NT_UNSTRUCTURED), SQL2, null)
                .getRows().iterator();
        assertRightOrder(nodes, results);

        setTraversalEnabled(true);
    }

    @Test
    public void oak2219() throws Exception {
        setTraversalEnabled(false);

        List<String> expected = new ArrayList<String>();
        Tree content = root.getTree("/").addChild("content");

        for (String s : of("a", "b", "c")) {
            Tree node = content.addChild(s);
            node.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, NAME);
            node.addChild("sub").setProperty(ORDERED_PROPERTY, s + "value", STRING);
            expected.add(node.getPath());
        }

        root.commit();

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + ")]", "xpath",
            expected);

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + " > 'avalue')]",
            "xpath", Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "a".compareTo(PathUtils.getName(path)) < 0;
                }
            })));

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + " >= 'bvalue')]",
            "xpath", Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "b".compareTo(PathUtils.getName(path)) <= 0;
                }
            })));

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + " <= 'bvalue')]",
            "xpath", Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "b".compareTo(PathUtils.getName(path)) >= 0;
                }
            })));

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + " < 'cvalue')]",
            "xpath", Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "c".compareTo(PathUtils.getName(path)) > 0;
                }
            })));

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY
                    + " > 'avalue' and sub/@" + ORDERED_PROPERTY + " < 'cvalue')]", "xpath",
            Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "c".compareTo(PathUtils.getName(path)) > 0
                           && "a".compareTo(PathUtils.getName(path)) < 0;
                }
            })));

        assertQuery("//element(*, nt:unstructured)[(sub/@" + ORDERED_PROPERTY + " = 'bvalue')]",
            "xpath", Lists.newArrayList(Iterables.filter(expected, new Predicate<String>() {
                @Override
                public boolean apply(String path) {
                    return "b".compareTo(PathUtils.getName(path)) == 0;
                }
            })));

        setTraversalEnabled(true);
    }
}


text:
extern crate cargotest;
extern crate cargo;
extern crate hamcrest;
extern crate toml;

use std::io::prelude::*;
use std::fs::{self, File};

use cargotest::cargo_process;
use cargotest::support::execs;
use cargotest::support::registry::registry;
use cargotest::install::cargo_home;
use cargo::util::config::Config;
use cargo::core::Shell;
use hamcrest::{assert_that, existing_file, is_not};

const TOKEN: &str = "test-token";
const CONFIG_FILE: &str = r#"
    [registry]
    token = "api-token"
"#;

fn setup_old_credentials() {
    let config = cargo_home().join("config");
    t!(fs::create_dir_all(config.parent().unwrap()));
    t!(t!(File::create(&config)).write_all(&CONFIG_FILE.as_bytes()));
}

fn setup_new_credentials() {
    let config = cargo_home().join("credentials");
    t!(fs::create_dir_all(config.parent().unwrap()));
    t!(t!(File::create(&config)).write_all(br#"
        token = "api-token"
    "#));
}

fn check_host_token(toml: toml::Value) -> bool {
    match toml {
        toml::Value::Table(table) => match table.get("token") {
            Some(v) => match v {
                &toml::Value::String(ref token) => (token.as_str() == TOKEN),
                _ => false,
            },
            None => false,
        },
        _ => false,
    }
}

#[test]
fn login_with_old_credentials() {
    setup_old_credentials();

    assert_that(cargo_process().arg("login")
                .arg("--host").arg(registry().to_string()).arg(TOKEN),
                execs().with_status(0));

    let config = cargo_home().join("config");
    assert_that(&config, existing_file());

    let mut contents = String::new();
    File::open(&config).unwrap().read_to_string(&mut contents).unwrap();
    assert!(CONFIG_FILE == &contents);

    let credentials = cargo_home().join("credentials");
    assert_that(&credentials, existing_file());

    contents.clear();
    File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
    assert!(check_host_token(contents.parse().unwrap()));
}

#[test]
fn login_with_new_credentials() {
    setup_new_credentials();

    assert_that(cargo_process().arg("login")
                .arg("--host").arg(registry().to_string()).arg(TOKEN),
                execs().with_status(0));

    let config = cargo_home().join("config");
    assert_that(&config, is_not(existing_file()));

    let credentials = cargo_home().join("credentials");
    assert_that(&credentials, existing_file());

    let mut contents = String::new();
    File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
    assert!(check_host_token(contents.parse().unwrap()));
}

#[test]
fn login_with_old_and_new_credentials() {
    setup_new_credentials();
    login_with_old_credentials();
}

#[test]
fn login_without_credentials() {
    assert_that(cargo_process().arg("login")
                .arg("--host").arg(registry().to_string()).arg(TOKEN),
                execs().with_status(0));

    let config = cargo_home().join("config");
    assert_that(&config, is_not(existing_file()));

    let credentials = cargo_home().join("credentials");
    assert_that(&credentials, existing_file());

    let mut contents = String::new();
    File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
    assert!(check_host_token(contents.parse().unwrap()));
}

#[test]
fn new_credentials_is_used_instead_old() {
    setup_old_credentials();
    setup_new_credentials();

    assert_that(cargo_process().arg("login")
                .arg("--host").arg(registry().to_string()).arg(TOKEN),
                execs().with_status(0));

    let config = Config::new(Shell::new(), cargo_home(), cargo_home());
    let token = config.get_string("registry.token").unwrap().map(|p| p.val);
    assert!(token.unwrap() == TOKEN);
}

redpajama_set_name: RedPajamaGithub


text:
<?php

namespace yolk;

// global helper functions (e.g. d() and dd())
require __DIR__.'/bootstrap.php';

Yolk::registerHelpers([
	'yolk\\helpers\\ArrayHelper',
	'yolk\\helpers\\StringHelper',
	'yolk\\helpers\\DateTimeHelper',
	'yolk\\helpers\\Inflector',
]);

class Yolk {

	const DUMP_TEXT     = 'text';
	const DUMP_HTML     = 'html';
	const DUMP_TERMINAL = 'terminal';

	/**
	 * The debug mode flag.
	 * @var boolean
	 */
	protected static $debug;

	/**
	 * Current error handler.
	 * @var callable
	 */
	protected static $error_handler = ['\\yolk\\exceptions\\Handler', 'error'];

	/**
	 * Current exception handler.
	 * @var callable
	 */
	protected static $exception_handler = ['\\yolk\\exceptions\\Handler', 'exception'];

	/**
	 * The error page to display for production web apps.
	 * @var string
	 */
	protected static $error_page;

	/**
	 * Array of helper methods accessable as static method on this class.
	 * @var array
	 */
	protected static $helpers = [];

	/**
	 * Cannot be instantiated.
	 */
	private function __construct() {}

	/**
	 * Determines if this is a command-line environment.
	 * @return boolean
	 */
	public static function isCLI() {
		return defined('STDIN') && is_resource(STDIN) && (get_resource_type(STDIN) == 'stream');
	}

	/**
	 * Determines if debug mode is enabled.
	 * @return boolean
	 */
	public static function isDebug() {
		return static::$debug;
	}

	/**
	 * Enables or disabled debug mode.
	 * @param boolean  $debug
	 * @return void
	 */
	public static function setDebug( $debug = false ) {
		static::$debug = (bool) $debug;
	}

	/**
	 * Pretty-print a variable - if running in debug mode.
	 * @param  mixed $var
	 * @param  string $format one of the Yolk::DUMP_* constants
	 * @return void
	 */
	public static function dump( $var, $format = null ) {

		if( !static::isDebug() )
			return;

		$dumpers = [
			static::DUMP_HTML     => '\\yolk\\debug\\HTMLDumper',
			static::DUMP_TERMINAL => '\\yolk\\debug\\TerminalDumper',
			static::DUMP_TEXT     => '\\yolk\\debug\\TextDumper',
		];

		// auto-detect format if unknown or none specified
		if( !isset($dumpers[$format]) )
			$format = static::isCLI() ? static::DUMP_TERMINAL : static::DUMP_HTML;

		$dumpers[$format]::dump($var);

	}

	/**
	 * Register one or multiple static helper classes.
	 * Static methods defined within each class will become statically callable
	 * via the Yolk object.
	 * .e.g yolk\helpers\ArrayHelper::sum() -> yolk\Yolk::sum()
	 * @param  string|array $classes
	 */
	public static function registerHelpers( $classes ) {

		if( !is_array($classes) )
			$classes = [$classes];

		foreach( $classes as $class ) {

			$class   = new \ReflectionClass($class);
			$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_STATIC);

			foreach( $methods as $m ) {
				static::addHelperMethod($class->name, $m->name);
			}

		}

	}

	/**
	 * Register a single static method as a helper.
	 * @param string $class
	 * @param string $method
	 */
	public static function addHelperMethod( $class, $method ) {

		$k = strtolower($method);

		if( method_exists(__CLASS__, $method) )
			throw new \Exception(sprintf("Helper methods cannot override pre-defined Yolk methods - '%s' is reserved", $method));
		elseif( isset(static::$helpers[$k]) && static::$helpers[$k][0] != $class )
			throw new \Exception(sprintf("Helper method '%s' already defined in class '%s', duplicate in '%s'", $method, static::$helpers[$k][0], $class));

		static::$helpers[$k] = [$class, $method];

	}

	/**
	 * Executes the specified closure, wrapping it in Yolk's error and exception handling.
	 * @param \Closure  $callable
	 * @return void
	 */
	public static function run( Callable $callable ) {

		try {

			// catch fatal errors
			register_shutdown_function(['\\yolk\\exceptions\\Handler', 'checkFatal']);

			// use our error handler
			$error_handler = set_error_handler(static::$error_handler);

			$args = func_get_args();
			array_shift($args);

			$result = call_user_func_array($callable, $args);

			// restore the original error handler
			// if $error_handler is null then passing it to set_error_handler() will fail on PHP < v5.5
			// so we create an empty error handler thereby causing PHP to run it's own handler.
			set_error_handler($error_handler ?: function() { return false; });

			return $result;

		}
		catch( \Exception $e ) {
			static::exception($e);
		}

	}

	/**
	 * Specifies the function to execute in the event of an error being triggered during a call to Yolk::run().
	 * @param callable $handler
	 * @return void
	 */
	public static function setErrorHandler( callable $handler = null ) {
		static::$error_handler = $handler ?: ['\\yolk\\exceptions\\Handler', 'error'];
	}

	/**
	 * Specifies the function to execute in the event of an exception being thrown during a call to Yolk::run().
	 * @param callable $handler
	 * @return void
	 */
	public static function setExceptionHandler( callable $handler = null ) {
		static::$exception_handler = $handler ?: ['\\yolk\\exceptions\\Handler', 'exception'];
	}

	/**
	 * Call the registered exception handler.
	 * @param  \Exception $error
	 * @return void
	 */
	public static function exception( \Exception $error ) {
		return call_user_func(
			static::$exception_handler,
			$error,
			static::$error_page ?: __DIR__. '/exceptions/error.php'
		);
	}

	/**
	 * Specifies the error page to display in the event of an error/exception.
	 *
	 * @param string  $file
	 * @return void
	 */
	public static function setErrorPage( $file ) {
		static::$error_page = (string) $file;
	}

	public static function __callStatic( $method, array $args = [] ) {

		$k = strtolower($method);

		if( !isset(static::$helpers[$k]) )
			throw new \BadMethodCallException("Unknown helper method '$method'");

		return call_user_func_array(static::$helpers[$k], $args);

	}

}

// EOF


text:
'use strict';

module.exports = function (app) {
  // Root routing
  var core = require('../../app/controllers/core');
  app.route('/').get(core.index);
};


text:
namespace Microsoft.Azure.Management.Monitor.Models
{
    using Microsoft.Rest;
    using Microsoft.Rest.Serialization;
    using Newtonsoft.Json;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// The log profile resource.
    /// </summary>
    [Rest.Serialization.JsonTransformation]
    public partial class LogProfileResource : Resource
    {
        /// <summary>
        /// Initializes a new instance of the LogProfileResource class.
        /// </summary>
        public LogProfileResource()
        {
            CustomInit();
        }

        /// <summary>
        /// Initializes a new instance of the LogProfileResource class.
        /// </summary>
        /// <param name="location">Resource location</param>
        /// <param name="locations">List of regions for which Activity Log
        /// events should be stored or streamed. It is a comma separated list
        /// of valid ARM locations including the 'global' location.</param>
        /// <param name="categories">the categories of the logs. These
        /// categories are created as is convenient to the user. Some values
        /// are: 'Write', 'Delete', and/or 'Action.'</param>
        /// <param name="retentionPolicy">the retention policy for the events
        /// in the log.</param>
        /// <param name="id">Azure resource Id</param>
        /// <param name="name">Azure resource name</param>
        /// <param name="type">Azure resource type</param>
        /// <param name="tags">Resource tags</param>
        /// <param name="kind">Metadata used by portal/tooling/etc to render
        /// different UX experiences for resources of the same type; e.g.
        /// ApiApps are a kind of Microsoft.Web/sites type.  If supported, the
        /// resource provider must validate and persist this value.</param>
        /// <param name="etag">The etag field is *not* required. If it is
        /// provided in the response body, it must also be provided as a header
        /// per the normal etag convention.  Entity tags are used for comparing
        /// two or more entities from the same requested resource. HTTP/1.1
        /// uses entity tags in the etag (section 14.19), If-Match (section
        /// 14.24), If-None-Match (section 14.26), and If-Range (section 14.27)
        /// header fields. </param>
        /// <param name="storageAccountId">the resource id of the storage
        /// account to which you would like to send the Activity Log.</param>
        /// <param name="serviceBusRuleId">The service bus rule ID of the
        /// service bus namespace in which you would like to have Event Hubs
        /// created for streaming the Activity Log. The rule ID is of the
        /// format: '{service bus resource ID}/authorizationrules/{key
        /// name}'.</param>
        public LogProfileResource(string location, IList<string> locations, IList<string> categories, RetentionPolicy retentionPolicy, string id = default(string), string name = default(string), string type = default(string), IDictionary<string, string> tags = default(IDictionary<string, string>), string kind = default(string), string etag = default(string), string storageAccountId = default(string), string serviceBusRuleId = default(string))
            : base(location, id, name, type, tags, kind, etag)
        {
            StorageAccountId = storageAccountId;
            ServiceBusRuleId = serviceBusRuleId;
            Locations = locations;
            Categories = categories;
            RetentionPolicy = retentionPolicy;
            CustomInit();
        }

        /// <summary>
        /// An initialization method that performs custom operations like setting defaults
        /// </summary>
        partial void CustomInit();

        /// <summary>
        /// Gets or sets the resource id of the storage account to which you
        /// would like to send the Activity Log.
        /// </summary>
        [JsonProperty(PropertyName = "properties.storageAccountId")]
        public string StorageAccountId { get; set; }

        /// <summary>
        /// Gets or sets the service bus rule ID of the service bus namespace
        /// in which you would like to have Event Hubs created for streaming
        /// the Activity Log. The rule ID is of the format: '{service bus
        /// resource ID}/authorizationrules/{key name}'.
        /// </summary>
        [JsonProperty(PropertyName = "properties.serviceBusRuleId")]
        public string ServiceBusRuleId { get; set; }

        /// <summary>
        /// Gets or sets list of regions for which Activity Log events should
        /// be stored or streamed. It is a comma separated list of valid ARM
        /// locations including the 'global' location.
        /// </summary>
        [JsonProperty(PropertyName = "properties.locations")]
        public IList<string> Locations { get; set; }

        /// <summary>
        /// Gets or sets the categories of the logs. These categories are
        /// created as is convenient to the user. Some values are: 'Write',
        /// 'Delete', and/or 'Action.'
        /// </summary>
        [JsonProperty(PropertyName = "properties.categories")]
        public IList<string> Categories { get; set; }

        /// <summary>
        /// Gets or sets the retention policy for the events in the log.
        /// </summary>
        [JsonProperty(PropertyName = "properties.retentionPolicy")]
        public RetentionPolicy RetentionPolicy { get; set; }

        /// <summary>
        /// Validate the object.
        /// </summary>
        /// <exception cref="ValidationException">
        /// Thrown if validation fails
        /// </exception>
        public override void Validate()
        {
            base.Validate();
            if (Locations == null)
            {
                throw new ValidationException(ValidationRules.CannotBeNull, "Locations");
            }
            if (Categories == null)
            {
                throw new ValidationException(ValidationRules.CannotBeNull, "Categories");
            }
            if (RetentionPolicy == null)
            {
                throw new ValidationException(ValidationRules.CannotBeNull, "RetentionPolicy");
            }
            if (RetentionPolicy != null)
            {
                RetentionPolicy.Validate();
            }
        }
    }
}


text: Android Library with common utilities and tools


text:
class AObject : public inspire::iObject
{
public:
   AObject() { std::cout << "A Object constructor" << std::endl; }
   ~AObject() { std::cout << "A Object destructor" << std::endl; }

   void say() { std::cout << "A Object says" << std::endl; }
};

class BObject : public inspire::iObject
{
public:
   BObject() { std::cout << "B Object constructor" << std::endl; }
   ~BObject() { std::cout << "B Object destructor" << std::endl; }

   void say() { _num = 100; std::cout << "B Object says" << _num << std::endl; }

private:
   int _num;
};

int main(int argc, char* argv)
{
   AObject* obj = INSPIRE_NEW AObject();
   INSPIRE_ASSERT(NULL != obj, "obj cannot be NULL");
   obj->say();
   INSPIRE_DEL obj;

   BObject a;
   BObject *pB = INSPIRE_NEW BObject();
   a.say();
   pB->say();
   return 0;
}


text:
<?php

/* TopxiaAdminBundle:Article:article-tr.html.twig */
class __TwigTemplate_419a47cd46825d0296582798bed4a9f5751ee607efad755fcdab6379c033dcb1 extends Twig_Template
{
    public function __construct(Twig_Environment $env)
    {
        parent::__construct($env);

        $this->parent = false;

        $this->blocks = array(
        );
    }

    protected function doDisplay(array $context, array $blocks = array())
    {
        $__internal_399abd5f190d6113f88c662250bbc79e56635c45c0623726cc4bcf9ba15d93bb = $this->env->getExtension("Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension");
        $__internal_399abd5f190d6113f88c662250bbc79e56635c45c0623726cc4bcf9ba15d93bb->enter($__internal_399abd5f190d6113f88c662250bbc79e56635c45c0623726cc4bcf9ba15d93bb_prof = new Twig_Profiler_Profile($this->getTemplateName(), "template", "TopxiaAdminBundle:Article:article-tr.html.twig"));

        // line 1
        $context["dict_macro"] = $this->loadTemplate("TopxiaWebBundle:Common:data-dict-macro.html.twig", "TopxiaAdminBundle:Article:article-tr.html.twig", 1);
        // line 2
        echo "
env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "html", null, true);
        echo "\">
  <input value=\"";
        // line 3
        echo twig_escape_filter($this->env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "html", null, true);
        echo "\" type=\"checkbox\" data-role=\"batch-item\"> ";
        echo twig_escape_filter($this->env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "html", null, true);
        echo "

      env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("article_detail", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
        echo "\" target=\"_blank\">";
        echo twig_escape_filter($this->env, twig_slice($this->env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "title", array()), 0, 22), "html", null, true);
        if ((twig_length_filter($this->env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "title", array())) > 22)) {
            echo "...";
        }
        echo "

  ";
        // line 9
        $this->loadTemplate("OrgBundle:Org/Parts:table-body-td.html.twig", "TopxiaAdminBundle:Article:article-tr.html.twig", 9)->display(array_merge($context, array("orgCode" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "orgCode", array()))));
        // line 10
        echo "
    ";
        // line 11
        if ((isset($context["category"]) ? $context["category"] : $this->getContext($context, "category"))) {
            // line 12
            echo "      env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("article_category", array("categoryCode" => $this->getAttribute((isset($context["category"]) ? $context["category"] : $this->getContext($context, "category")), "code", array()))), "html", null, true);
            echo "\" target=\"_blank\">";
            echo twig_escape_filter($this->env, $this->getAttribute((isset($context["category"]) ? $context["category"] : $this->getContext($context, "category")), "name", array()), "html", null, true);
            echo "
    ";
        } else {
            // line 14
            echo "      ";
            echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("未分类"), "html", null, true);
            echo "
    ";
        }
        // line 16
        echo "
  ";
        // line 17
        echo twig_escape_filter($this->env, twig_date_format_filter($this->env, $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "publishedTime", array()), "Y-n-d H:i"), "html", null, true);
        echo "

    env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("设置为头条"), "html", null, true);
        echo "\"
       data-set-url=\"";
        // line 21
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_set_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "featured")), "html", null, true);
        echo "\"
       data-cancel-url=\"";
        // line 22
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_cancel_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "featured")), "html", null, true);
        echo "\">
      getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "featured", array()) == 1)) {
            echo "label-success";
        } else {
            echo "label-default";
        }
        echo "\">";
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("头"), "html", null, true);
        echo "
    env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("推荐该资讯"), "html", null, true);
        echo "\"
       data-set-url=\"";
        // line 26
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_set_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "promoted")), "html", null, true);
        echo "\"
       data-cancel-url=\"";
        // line 27
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_cancel_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "promoted")), "html", null, true);
        echo "\">
      getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "promoted", array()) == 1)) {
            echo "label-success";
        } else {
            echo "label-default";
        }
        echo "\">";
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("荐"), "html", null, true);
        echo "

    env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("置顶该资讯"), "html", null, true);
        echo "\"
       data-set-url=\"";
        // line 32
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_set_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "sticky")), "html", null, true);
        echo "\"
       data-cancel-url=\"";
        // line 33
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_cancel_property", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()), "property" => "sticky")), "html", null, true);
        echo "\">
      getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "sticky", array()) == 1)) {
            echo "label-success";
        } else {
            echo "label-default";
        }
        echo "\">";
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("顶"), "html", null, true);
        echo "

  ";
        // line 38
        echo $context["dict_macro"]->getarticleStatus($this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "status", array()));
        echo "

      env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_edit", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
        echo "\" class=\"btn btn-default btn-sm\">";
        echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("编辑"), "html", null, true);
        echo "

        ";
        // line 47
        if (($this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "status", array()) != "published")) {
            // line 48
            echo "
env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_publish", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
            echo "\">";
            echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("发布"), "html", null, true);
            echo "

        ";
        }
        // line 51
        echo "        ";
        if (($this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "status", array()) == "published")) {
            // line 52
            echo "
env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_unpublish", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
            echo "\">";
            echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("取消发布"), "html", null, true);
            echo "

        ";
        }
        // line 55
        echo "        ";
        if (($this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "status", array()) != "trash")) {
            // line 56
            echo "
env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_trash", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
            echo "\">";
            echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("移至回收站"), "html", null, true);
            echo "

        ";
        }
        // line 59
        echo "        ";
        if (($this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "status", array()) == "trash")) {
            // line 60
            echo "
env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\RoutingExtension')->getPath("admin_article_delete", array("id" => $this->getAttribute((isset($context["article"]) ? $context["article"] : $this->getContext($context, "article")), "id", array()))), "html", null, true);
            echo "\">";
            echo twig_escape_filter($this->env, $this->env->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->trans("永久删除"), "html", null, true);
            echo "

        ";
        }
        // line 63
        echo "

";

        $__internal_399abd5f190d6113f88c662250bbc79e56635c45c0623726cc4bcf9ba15d93bb->leave($__internal_399abd5f190d6113f88c662250bbc79e56635c45c0623726cc4bcf9ba15d93bb_prof);

    }

    public function getTemplateName()
    {
        return "TopxiaAdminBundle:Article:article-tr.html.twig";
    }

    public function isTraitable()
    {
        return false;
    }

    public function getDebugInfo()
    {
        return array (  218 => 63,  211 => 61,  208 => 60,  205 => 59,  198 => 57,  195 => 56,  192 => 55,  185 => 53,  182 => 52,  179 => 51,  172 => 49,  169 => 48,  167 => 47,  157 => 42,  150 => 38,  137 => 34,  133 => 33,  129 => 32,  125 => 31,  113 => 28,  109 => 27,  105 => 26,  101 => 25,  90 => 23,  86 => 22,  82 => 21,  78 => 20,  72 => 17,  69 => 16,  63 => 14,  55 => 12,  53 => 11,  50 => 10,  48 => 9,  37 => 6,  29 => 3,  24 => 2,  22 => 1,);
    }

    public function getSource()
    {
        return "{% import \"TopxiaWebBundle:Common:data-dict-macro.html.twig\" as dict_macro %}

  <input value=\"{{ article.id }}\" type=\"checkbox\" data-role=\"batch-item\"> {{ article.id }}

      {{ article.title | slice(0,22) }}{% if article.title|length >22 %}...{% endif %}

  {% include 'OrgBundle:Org/Parts:table-body-td.html.twig' with {orgCode: article.orgCode}%}

    {% if category %}
      {{ category.name }}
    {% else %}
      {{'未分类'|trans}}
    {% endif %}

  {{ article.publishedTime|date('Y-n-d H:i') }}

      {{'头'|trans}}

      {{'荐'|trans}}

      {{'顶'|trans}}

  {{ dict_macro.articleStatus(article.status) }}

      {{'编辑'|trans}}

        {% if article.status != 'published' %}

{{'发布'|trans}}

        {% endif %}
        {% if article.status == 'published' %}

{{'取消发布'|trans}}

        {% endif %}
        {% if article.status != 'trash' %}

{{'移至回收站'|trans}}

        {% endif %}
        {% if article.status == 'trash' %}

{{'永久删除'|trans}}

        {% endif %}

";
    }
}


text:
<?php

use Hubzero\Content\Migration\Base;

// No direct access
defined('_HZEXEC_') or die();

/**
 * Migration script for adding component entry for com_system
 **/
class Migration20170831000000ComSystem extends Base
{
	/**
	 * Up
	 **/
	public function up()
	{
		// Create component entry but do NOT create a menu item as
		// com_system is purposely separate from the 'components' list.
		//
		// string  $name            Component name
		// string  $option          com_xyz
		// int     $enabled         Whether or not the component should be enabled
		// string  $params          Component params (if already known)
		// bool    $createMenuItem  Create an admin menu item for this component
		$this->addComponentEntry('system', null, 1, '', false);
	}

	/**
	 * Down
	 **/
	public function down()
	{
		$this->deleteComponentEntry('system');
	}
}


text:
package com.rincon.jdebug;

/**
 * @author David Moss (dmm@rincon.com)
 */

import com.rincon.jdebug.receiver.JDebugEvents;
import com.rincon.jdebug.receiver.JDebugReceiver;

public class JDebug implements JDebugEvents {

	JDebugReceiver receiver = new JDebugReceiver();

	public JDebug() {
		receiver.addListener(this);
	}

	/**
	 * A JDebug message has been received
	 */
	public void messageReceived(String message) {
		System.out.println(message);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new JDebug();
	}

}


text:
using namespace std;

int main () {
  ifstream myfile ("example.txt");
  vector<double> EField;
  vector<double> alpha;
  double value;

  if (myfile.is_open())
  {
    while ( myfile >> value )
    {
      EField.push_back(value);
      myfile >> value;
      alpha.push_back(value);
    }
    myfile.close();
  }

  else cout << "Unable to open file";

  for (vector<double>::iterator it_E = EField.begin(), it_alpha = alpha.begin() ; it_E != EField.end() && it_alpha != alpha.end(); ++it_E, ++it_alpha)
    cout << ' ' << *it_E << ' ' << *it_alpha << endl;

  return 0;
}


text:
import * as Boom from '@hapi/boom';
import {
  Lifecycle,
  Plugin,
  Request,
  ResponseObject,
  ResponseToolkit,
  ResponseValue,
  ServerRequestExtType,
} from '@hapi/hapi';
import * as Joi from '@hapi/joi';
import * as Limitus from 'limitus';
import { Bucket } from './bucket';
import { build as buildLimitus } from './limitus';

const schema = Joi.object()
  .keys({
    buckets: Joi.array().required(),
    default: Joi.array()
      .items(Joi.string())
      .required(),
    includeHeaders: Joi.bool().required(),
    enabled: Joi.bool().required(),
    cache: Joi.string().optional(),
    limitus: Joi.object().optional(),
    exclude: Joi.func().required(),
    onLimit: Joi.func().required(),
    onPass: Joi.func().required(),
    event: Joi.string().valid(['onRequest', 'onPreAuth', 'onPostAuth']),
    timeout: Joi.object().optional(),
  })
  .required();

declare module '@hapi/hapi' {
  export interface PluginSpecificConfiguration {
    yaral?: IYaralRouteOptions | string | string[];
  }
  export interface PluginsStates {
    yaral?: IYaralInternalData & {
      limited: boolean;
    };
  }
}

export interface IYaralRouteOptions {
  /**
   * specifies the bucket `name` or array of of the rate limit buckets to use in addition to the configured `default` rules.
   * Buckets are matched first to last.
   */
  buckets?: string | string[];
  /**
   * `enabled` is a boolean which allows you to override a true `enabled` global configuration.
   * This can be used to exclude routes from global rate limits.
   * Defaults to `true`.
   */
  enabled?: boolean;
  /**
   * If both a route-level and a global exclude passed, the request will be excluded if _either_ return true.
   */
  exclude?(req: Request): boolean;
}

/**
 * cancel is a symbol that can be returned from onLimit in order to abort
 * rate limiting the provided request.
 */
export const cancel: unique symbol = Symbol('cancel');

/**
 * Describes a rate limit bucket.
 */
export interface IBucketOptions {
  /**
   * Identifier
   */
  name: string;
  /**
   * An `interval` that allows a `max` number of requests.
   */
  interval: number;
  max: number;
  /**
   * A `mode` as described in the Limitus documentation.
   * Defaults to `interval`.
   */
  mode?: 'continuous' | 'interval';
  /**
   * Function that takes a Hapi request object and returns a string, number or object that identifies the requester.
   */
  id(req: Request): string | number | object;
  /**
   * A list of `codes` that specify response codes that count towards this bucket's limit.
   * Responses not in this range will not be limited.
   * Defaults to `['2xx', '3xx']`.
   * *Tip:* to limit all responses, use `['xxx']`.
   */
  codes?: string[];
}

/**
 * Options that are available when you register Yaral.
 */
export interface IYaralOptions {
  /**
   * is a bucket `name` or array of names of the bucket applied to all routes.
   * Defaults to `[]`. Buckets are matched first to last.
   */
  default?: string | string[];
  /**
   * is the cache name (as configured in the Hapi server) used to store rate limiting data.
   * Defaults to the server's default cache.
   */
  cache?: string;
  /**
   * is a boolean whether to enable rate limiting.
   * Useful to disable limiting in tests and development.
   * Default to `true`.
   */
  enabled?: boolean;
  /**
   * specifies whether rate limit headers should be included in the response.
   */
  includeHeaders?: boolean;
  /**
   * A Limitus instance to use for this rate limiting. Defaults to `new Limitus()`.
   */
  limitus?: Limitus;

  /**
   * A function, called with the `request` object that returns true if the provided request should be omitted from limiting.
   */
  exclude?(req: Request): boolean;

  /**
   * A function called with the `request` object with a successful request is made which is not rate limited.
   */
  onPass?(req: Request): void;

  /**
   * A function called with the `request` object, `rule` name that failed, and extra `data` that rule
   * returns when a request is made which does get rate limited.
   * You may return `yaral.cancel` from this method to cause the specific request not to be rate limited.
   */
  onLimit?(
    req: Request,
    tk: ResponseToolkit,
    data: Limitus.DropInfo,
    reset: number,
    headers: { [key: string]: string | string[] },
  ): ResponseValue | typeof cancel | void;

  /**
   * is an array of interval/mode config for [Limitus](https://github.com/MCProHosting/limitus#limitusrulename-rule) intervals.
   * Each item should have:
   */
  buckets: IBucketOptions[];
  /**
   * A string representing when in the request lifecycle the limit checks should occur
   */
  event?: ServerRequestExtType;

  /**
   * JSON object containing redis-connection-timeout settings (enabled, timeout in ms)
   */
  timeout?: { enabled: boolean; timeout: number; ontimeout?: Lifecycle.Method };
}

export interface IYaralInternalData {
  ids: (string | number | object)[];
  buckets: string[];
}

export class TimeoutError extends Error {}

export const plugin: Plugin<IYaralOptions> = {
  async register(server, options) {
    options = {
      cache: '_default',
      enabled: true,
      includeHeaders: true,
      default: [],
      exclude: () => false,
      onLimit: () => {
        /*do nothing*/
      },
      onPass: () => {
        /*do nothing*/
      },
      event: 'onPreAuth',
      //Timeout enabled by default with a value of 1000 ms. On timeout, by default we continue.
      timeout: {
        enabled: true,
        timeout: 1000,
        ontimeout: (_request, reply) => reply.continue,
      },
      ...options,
    };

    Joi.assert(options, schema);

    // If we aren't enabled, don't bother doing anything.
    if (!options.enabled) {
      return;
    }

    const limitus = options.limitus || buildLimitus(server, options.cache);
    const buckets: {
      [key: string]: Bucket;
    } = {};
    options.buckets.forEach(bucket => {
      const b = new Bucket(bucket, limitus);
      buckets[b.name()] = b;
    });

    /**
     * Returns a configuration object for the route based on its specific
     * rules.
     */
    function resolveRouteOpts(
      req: Request,
    ): {
      enabled: boolean;
      buckets: string[];
      exclude: (req: Request) => boolean;
    } {
      const routeOpts = req.route.settings.plugins.yaral;

      const opts = {
        enabled: true,
        buckets: <string[]>[],
        exclude: (_req: Request) => false,
      };
      if (!routeOpts) {
        // do nothing
      } else if (typeof routeOpts === 'string') {
        // specifying bucket as string
        opts.buckets = [routeOpts];
      } else if (Array.isArray(routeOpts)) {
        // specifying array of buckets
        opts.buckets = routeOpts;
      } else {
        // providing a literal object
        Object.assign(opts, routeOpts);
        if (typeof opts.buckets === 'string') {
          opts.buckets = [opts.buckets];
        }
      }

      if (opts.enabled) {
        opts.buckets = opts.buckets.concat(options.default);
        opts.enabled =
          !(options.exclude(req) || opts.exclude(req)) &&
          opts.buckets.length + options.default.length > 0;
      }
      return opts;
    }

    /**
     * Returns the bucket used to rate limit the specified response.
     */
    const matchBucket = (
      info: IYaralInternalData,
      res: ResponseObject,
    ): {
      bucket: Bucket;
      id: string | number | object;
    } => {
      for (let i = 0; i < info.buckets.length; i++) {
        const bucket = buckets[info.buckets[i]];
        if (bucket.matches(res.statusCode)) {
          return { bucket: bucket, id: info.ids[i] };
        }
      }

      return undefined;
    };

    /**
     * Adds rate limit headers to the response if they're set.
     */
    const addHeaders = (
      res: Boom.Output | ResponseObject,
      headers: { [key: string]: string | number },
    ) => {
      if (options.includeHeaders) {
        Object.assign(res.headers, headers);
      }
    };

    const getRequestLogDetails = (
      err: Error,
      req: Request,
      isTimedout: boolean,
      duration: number,
    ) => {
      return {
        name: 'yaral-timeout',
        url: req.url.href || '',
        duration: duration,
        success: !err,
        properties: {
          error: err ? err.stack : '',
          isTimedout: isTimedout,
        },
      };
    };

    async function createTimeout<T>(call: () => Promise<T>, req: Request): Promise<T> {
      const startTime = Date.now();
      const p = call();
      if (!options.timeout.enabled) {
        return p;
      }
      try {
        return await new Promise<T>((resolve, reject) => {
          const t = setTimeout(
            () => reject(new TimeoutError('Call Timed Out')),
            options.timeout.timeout,
          );
          p.then(innerV => {
            clearTimeout(t);
            resolve(innerV);
          }, reject);
        });
      } catch (e) {
        server.log(
          ['ratelimit', 'timeout'],
          getRequestLogDetails(e, req, true, Date.now() - startTime),
        );
        throw e;
      }
    }

    //Appropriately handles different types of Errors
    const handleError = (err: Error, req: Request, reply: ResponseToolkit) => {
      //In case there is a redis timeout continue executing
      //did not put it in the same block as Limitus.Rejected for the sake of future logging
      if (err instanceof TimeoutError) {
        server.log(
          ['ratelimit', 'timeout'],
          getRequestLogDetails(err, req, true, options.timeout.timeout),
        );
        return options.timeout.ontimeout(req, reply);
      }

      if (!(err instanceof Limitus.Rejected)) {
        server.log(['error', 'ratelimit'], err);
      }

      // Internal errors should not halt everything.
      return reply.continue;
    };

    server.ext(options.event, async (req, tk) => {
      const opts = resolveRouteOpts(req);
      if (opts.enabled === false) {
        return tk.continue;
      }

      const info = {
        buckets: opts.buckets,
        ids: opts.buckets.map(b => buckets[b].identify(req)),
        limited: false,
      };
      req.plugins.yaral = info;
      try {
        await Promise.all(
          info.buckets.map((name, i) =>
            createTimeout(() => limitus.checkLimited(name, info.ids[i]), req),
          ),
        );
        options.onPass(req);
        return tk.continue;
      } catch (err) {
        //Internal Error or Timeout Error
        if (!(err instanceof Limitus.Rejected)) {
          return handleError(err, req, tk);
        }

        // FIXME: limitus: this is actually a number??
        const reset: number = <any>err.bucketName;

        const headers = {
          'X-RateLimit-Remaining': '0',
          'X-RateLimit-Reset': reset.toString(),
        };

        // Continue the request if onLimit dictates that we cancel limiting.
        const lResponse = options.onLimit(req, tk, err.info, reset, headers);
        if (lResponse) {
          if (lResponse === cancel) {
            return tk.continue;
          }
          return lResponse;
        }

        info.limited = true;
        const res = Boom.tooManyRequests();
        addHeaders(res.output, headers);
        throw res;
      }
    });

    server.ext('onPreResponse', async (req, reply) => {
      const res = (<Boom<any>>req.response).output || <ResponseObject>req.response;
      const opts = req.plugins.yaral && matchBucket(req.plugins.yaral, <ResponseObject>res);
      if (!opts || req.plugins.yaral.limited) {
        return reply.continue;
      }
      try {
        const data = await createTimeout(() => limitus.drop(opts.bucket.name(), opts.id), req);
        addHeaders(res, opts.bucket.headers(data));
        return reply.continue;
      } catch (err) {
        return handleError(err, req, reply);
      }
    });
  },
  pkg: require('../package'),
};


text:
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

// List of observers.
$observers = array(
    array(
        'eventname'   => '\mod_assign\event\submission_created',
        'callback'    => '\assignfeedback_editpdf\event\observer::submission_created',
    ),
    array(
        'eventname'   => '\mod_assign\event\submission_updated',
        'callback'    => '\assignfeedback_editpdf\event\observer::submission_updated',
    ),
);


text:
package sage;

/*
 * This represents a user interface instance which may be remote or local. This is implemented
 * by the UIManaged for local UIs and by an inner class of SageTVConnection for remote UIs
 */
public interface UIClient
{
  public static final int LOCAL = 1;
  public static final int REMOTE_CLIENT = 2;
  public static final int REMOTE_UI = 3;
  public String getFullUIClientName();
  public String getLocalUIClientName();
  // Hooks processed through here are NOT rethreaded
  public Object processUIClientHook(String hookName, Object[] hookVars);
  public int getUIClientType();
  // This is the original clientName we used to use and is specific to a SageTVClient connection
  public String getUIClientHostname();

  // Allows to query the client for capabilities
  public String getCapability(String capability);
}


text: Rename a column.