/*
 * Copyright Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags and
 * the COPYRIGHT.txt file distributed with this work.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.teiid.translator.mongodb;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Iterator;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.teiid.CommandContext;
import org.teiid.cdk.api.TranslationUtility;
import org.teiid.core.util.ObjectConverterUtil;
import org.teiid.core.util.UnitTestUtil;
import org.teiid.language.Command;
import org.teiid.mongodb.MongoDBConnection;
import org.teiid.query.metadata.TransformationMetadata;
import org.teiid.query.unittest.RealMetadataFactory;
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.UpdateExecution;

import com.mongodb.*;

@SuppressWarnings("nls")
public class TestMongoDBUpdateExecution {
    private MongoDBExecutionFactory translator;
    private TranslationUtility utility;

    @Before
    public void setUp() throws Exception {
        this.translator = new MongoDBExecutionFactory();
        this.translator.start();

        TransformationMetadata metadata = RealMetadataFactory.fromDDL(ObjectConverterUtil.convertFileToString(UnitTestUtil.getTestDataFile("northwind.ddl")), "sakila", "northwind");
        this.utility = new TranslationUtility(metadata);
    }

    @Test
    public void testSimpleInsertNoAssosiations() throws Exception {
        String query = "insert into Customers (CustomerID,CompanyName,ContactName,"
                + "ContactTitle,Address,City,Region,"
                + "PostalCode,Country,Phone,Fax) VALUES ('11', 'jboss', 'teiid', 'Mr.Lizard', "
                + "'jboss.org', 'internet', 'all', '1111', 'US', '555-1212', '555-1212')";

        DBCollection dbCollection = helpUpdate(query, new String[]{"Customers"}, null, null);

        BasicDBObject result = new BasicDBObject();
        result.append("_id", "11");
        result.append("CompanyName", "jboss");
        result.append("ContactName" , "teiid");
        result.append("ContactTitle","Mr.Lizard");
        result.append("Address","jboss.org");
        result.append("City","internet");
        result.append("Region","all");
        result.append("PostalCode","1111");
        result.append( "Country","US");
        result.append("Phone","555-1212");
        result.append("Fax","555-1212");
        Mockito.verify(dbCollection).insert(result, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.never()).update(new BasicDBObject(), result, false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testEmbeddableInsert() throws Exception {
        String query = "INSERT INTO Categories (CategoryID, CategoryName, Description, Picture) " +
                "VALUES (12, 'mongo', 'mongo with sql', null)";

        DBCollection dbCollection = helpUpdate(query, new String[]{"Categories"}, null, null);
        BasicDBObject result = new BasicDBObject();
        result.append("_id", 12);
        result.append("CategoryName", "mongo");
        result.append("Description" , "mongo with sql");
        result.append("Picture",null);
        Mockito.verify(dbCollection).insert(result, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.never()).update(new BasicDBObject(), result, false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testMergeInsert() throws Exception {
        // tests one-to-many situation
        String query = "INSERT INTO OrderDetails (odID, ProductID, UnitPrice, Quantity, Discount) " +
                "VALUES (14, 15, 34.50, 10, 12)";

        BasicDBObject match = new BasicDBObject("_id", 14);
        DBCollection dbCollection = helpUpdate(query, new String[]{"Orders"}, match, null);

        BasicDBObject details = new BasicDBObject();
        BasicDBObject pk = new BasicDBObject();
        pk.append("odID", 14);
        pk.append("ProductID", 15);

        details.append("UnitPrice", 34.50);
        details.append("Quantity", 10);
        details.append("Discount", 12.0);
        details.append("_id", pk);

        details = new BasicDBObject("OrderDetails", details);
        Mockito.verify(dbCollection, Mockito.never()).insert(details, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$push", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeInsertOne2One() throws Exception {
        String query = "INSERT INTO address (cust_id, street, zip) VALUES (100, '123 Street', '90210')";

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, null);

        BasicDBObject details = new BasicDBObject();

        details.append("street", "123 Street");
        details.append("zip", "90210");

        details = new BasicDBObject("address", details);
        Mockito.verify(dbCollection, Mockito.never()).insert(details, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-many mapping update merge case;
    public void testMergeInsertOne2Many() throws Exception {
        String query = "INSERT INTO Notes (CustomerId, PostDate, Comment) VALUES (100, null, 'Hello')";

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, null);

        BasicDBObject details = new BasicDBObject();
        details.append("PostDate", null);
        details.append("Comment", "Hello");

        details = new BasicDBObject("Notes", details);
        Mockito.verify(dbCollection, Mockito.never()).insert(details, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$push", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeDeleteOne2OneonPK() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM address WHERE cust_id = 100";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBObject address = new BasicDBObject();
        address.append("street", "123 Street").append("zip", "90210").append("_id", new DBRef(null, "customer", 100));
        results.add(new BasicDBObject("_id", 100).append("address", address));

        DBObject match = new BasicDBObject("_id", 100);

        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("address", "");
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$unset", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeDeleteOne2OneonNonPK() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM address WHERE street = 'Highway 100'";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBObject address = new BasicDBObject();
        address.append("street", "123 Street").append("zip", "90210").append("_id", new DBRef(null, "customer", 100));
        results.add(new BasicDBObject("_id", 100).append("address", address));

        DBObject match = QueryBuilder.start("address.street").is("Highway 100").get();
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("address", "");
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$unset", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeDeleteOne2OneAllRows() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM address";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBObject address = new BasicDBObject();
        address.append("street", "123 Street").append("zip", "90210").append("_id", new DBRef(null, "customer", 100));
        results.add(new BasicDBObject("_id", 100).append("address", address));

        DBObject match = QueryBuilder.start("address").exists(true).get();
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("address", "");
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$unset", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test(expected=TranslatorException.class) // one-2-one mapping update merge case;
    public void testMergeDeleteOne2ManyWithORClause() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM Notes WHERE CustomerId = 100 OR Comment IN ('Hello', 'Hola')";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("Comment", "Hello").append("PostDate", "{ts '2014-07-15 09:06:04'}"));
        row.add(new BasicDBObject("Comment", "Hola").append("PostDate", "{ts '2013-07-15 09:06:04'}"));
        results.add(new BasicDBObject("_id", 100).append("Notes", row));

        BasicDBList commentIN = new BasicDBList();
        commentIN.add("Hello");
        commentIN.add("Hola");
        DBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("Notes", QueryBuilder.start("Comment").in(commentIN).get());
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$pull", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeDeleteOne2ManyWithIN() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM Notes WHERE Comment IN ('Hello', 'Hola')";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("Comment", "Hello").append("PostDate", "{ts '2014-07-15 09:06:04'}"));
        row.add(new BasicDBObject("Comment", "Hola").append("PostDate", "{ts '2013-07-15 09:06:04'}"));
        results.add(new BasicDBObject("_id", 100).append("Notes", row));

        BasicDBList commentIN = new BasicDBList();
        commentIN.add("Hello");
        commentIN.add("Hola");
        DBObject match = QueryBuilder.start("Notes").exists(true).get();
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("Notes", QueryBuilder.start("Comment").in(commentIN).get());
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$pull", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-one mapping update merge case;
    public void testMergeDeleteOne2ManyNonFK() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM Notes WHERE Comment = 'Hello'";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("Comment", "Hello").append("PostDate", "{ts '2014-07-15 09:06:04'}"));
        row.add(new BasicDBObject("Comment", "Hola").append("PostDate", "{ts '2013-07-15 09:06:04'}"));
        results.add(new BasicDBObject("_id", 100).append("Notes", row));

        DBObject match = QueryBuilder.start("Notes").exists(true).get();
        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, match, results);

        BasicDBObject details = new BasicDBObject("Notes", new BasicDBObject("Comment", "Hello"));
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$pull", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    private DBCollection helpUpdate(String query, String[] expectedCollection, DBObject match_result, ArrayList<DBObject> results) throws TranslatorException {
        Command cmd = this.utility.parseCommand(query);
        ExecutionContext context = Mockito.mock(ExecutionContext.class);
        CommandContext cc = Mockito.mock(CommandContext.class);
        Mockito.stub(context.getCommandContext()).toReturn(cc);
        MongoDBConnection connection = Mockito.mock(MongoDBConnection.class);
        DB db = Mockito.mock(DB.class);
        DBCollection dbCollection = Mockito.mock(DBCollection.class);
        for(String collection:expectedCollection) {
            Mockito.stub(db.getCollection(collection)).toReturn(dbCollection);
        }
        Mockito.stub(db.collectionExists(Mockito.anyString())).toReturn(true);
        Mockito.stub(connection.getDatabase()).toReturn(db);

        Mockito.stub(db.getCollectionFromString(Mockito.anyString())).toReturn(dbCollection);
        Mockito.stub(dbCollection.findOne(Mockito.any(BasicDBObject.class))).toReturn(match_result);
        WriteResult result = Mockito.mock(WriteResult.class);
        Mockito.stub(result.getN()).toReturn(1);
        Mockito.stub(dbCollection.insert(Mockito.any(BasicDBObject.class), Mockito.any(WriteConcern.class)))
            .toReturn(result);
        Mockito.stub(dbCollection.update(Mockito.any(BasicDBObject.class),
                Mockito.any(BasicDBObject.class),
                Mockito.eq(false),
                Mockito.eq(true),
                Mockito.any(WriteConcern.class))).toReturn(result);

        if (results != null) {
            Cursor out = new ResultsCursor(results);
            for (DBObject obj:results) {
                Mockito.stub(dbCollection.aggregate(Mockito.anyList(), Mockito.any(AggregationOptions.class))).toReturn(out);
                Mockito.stub(dbCollection.aggregate(Mockito.anyList(), Mockito.any(AggregationOptions.class))).toReturn(out);
            }
        }

        UpdateExecution execution = this.translator.createUpdateExecution(cmd, context, this.utility.createRuntimeMetadata(), connection);
        execution.execute();
        return dbCollection;
    }

    private static class ResultsCursor implements Cursor {
        private Iterator<DBObject> rows;

        ResultsCursor (ArrayList<DBObject> rows){
            this.rows = rows.iterator();
        }

        @Override
        public void remove() {
            this.rows.remove();
        }

        @Override
        public DBObject next() {
            return rows.next();
        }

        @Override
        public boolean hasNext() {
            return this.rows.hasNext();
        }

        @Override
        public ServerAddress getServerAddress() {
            return null;
        }

        @Override
        public long getCursorId() {
            return 0;
        }

        @Override
        public void close() {
        }
    }

    @Test
    public void testUpdateEmbeddable() throws Exception {
        // check to make sure about multiple updates
        String query = "UPDATE Categories SET CategoryName='JBOSS' WHERE CategoryID = 11";

        BasicDBObject match = new BasicDBObject("_id", 11);
        BasicDBObject details = new BasicDBObject();
        details.append("CategoryName", "JBOSS");
        details = new BasicDBObject("$set", details);

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        results.add(new BasicDBObject("_id", 11).append("key", "value"));

        DBCollection dbCollection = helpUpdate(query, new String[]{"Categories", "Products"}, match, results);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, details, false, true, WriteConcern.ACKNOWLEDGED);

        Mockito.verify(dbCollection).update(
                new BasicDBObject("CategoryID", 11), new BasicDBObject("$set", new BasicDBObject("Categories", new BasicDBObject("key","value"))),
                false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Ignore // the composite key with nesting is not going to work when both keys are not in the parent.
    @Test // one to many
    public void testDeleteMergeOneToManyOnPK() throws Exception {
        String query = "DELETE FROM OrderDetails WHERE ProductID = 14 and odID = 1";

        DBCollection dbCollection = helpUpdate(query, new String[]{"Orders"}, new BasicDBObject("oid", 1), null);
        DBObject match = QueryBuilder.start().and(new BasicDBObject("_id.ProductID", 14), new BasicDBObject("_id.odID", 1)).get();
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(1)).update(
                new BasicDBObject("_id", 1), new BasicDBObject("$pull", new BasicDBObject("OrderDetails", match)),
                false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Ignore // the composite key with nesting is not going to work when both keys are not in the parent.
    @Test // one to many
    public void testDeleteMergeOneToManyOnPKANDColumn() throws Exception {
        String query = "DELETE FROM OrderDetails WHERE ProductID = 14 and UnitPrice > 23.89";

        DBCollection dbCollection = helpUpdate(query, new String[]{"Orders"}, QueryBuilder.start().exists("OrderDetails").get(), null);

        QueryBuilder qb = QueryBuilder.start("OrderDetails").exists(true);

        QueryBuilder pullQuery = new QueryBuilder();
        pullQuery.and(new BasicDBObject("_id.ProductID", 14), QueryBuilder.start("UnitPrice").greaterThan(23.89).get());

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(1)).update(
                qb.get(), new BasicDBObject("$pull", new BasicDBObject("OrderDetails", pullQuery.get())),
                false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one to many
    @Ignore
    public void testMergeOne2ManyUpdateComplexKey() throws Exception {
        String query = "UPDATE OrderDetails SET UnitPrice = 12.50 WHERE ProductID = 14 and odID = 1";

        BasicDBObject pk= new BasicDBObject("ProductID", 14);
        BasicDBObject pk2= new BasicDBObject("ProductID", 15);

        BasicDBList result = new BasicDBList();
        result.add(new BasicDBObject().append("_id", pk).append("UnitPrice", 99.00).append("Quantity", 11));
        result.add(new BasicDBObject().append("_id", pk2).append("UnitPrice", 0.99).append("Quantity", 12));

        BasicDBObject row = new BasicDBObject("OrderDetails", result).append("_id", 1);

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        results.add(row);

        DBCollection dbCollection = helpUpdate(query, new String[]{"Orders"}, new BasicDBObject("oid", 1), results );
        // { "$set" : { "OrderDetails" : [ { "_id" : 1 , "ProductID" : 14 , "odID" : 1 , "UnitPrice" : 12.5}]}},
        BasicDBList expected = new BasicDBList();
        expected.add(new BasicDBObject("_id", pk).append("UnitPrice", 12.50).append("Quantity", 11));
        expected.add(new BasicDBObject().append("_id", pk2).append("UnitPrice", 0.99).append("Quantity", 12));

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(1)).update(
                new BasicDBObject("_id", 1), new BasicDBObject("$set", new BasicDBObject("OrderDetails", expected)),
                false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void tesNestedEmbeddingInsert() throws Exception {
        String query = "insert into T1 (e1, e2, e3) VALUES (1, 2, 3)";

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 2);

        DBCollection dbCollection = helpUpdate(query, new String[]{"T1", "T2"}, match, null);

        BasicDBObject row = new BasicDBObject();
        row.append("e1", 1);
        row.append("e3", 3);
        row.append("_id", 2);
        row.append("T2", match);

        Mockito.verify(dbCollection).insert(row, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.never()).update(match, row, false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    @Ignore
    public void tesNestedEmbeddingUpdate() throws Exception {
        String query = "UPDATE T3 SET e2 = 2, e3 = 3 WHERE e1 = 1";

        BasicDBObject t3_match = new BasicDBObject();
        t3_match.append("_id", 1);

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        results.add(new BasicDBObject("_id", 1).append("key", "value"));

        DBCollection dbCollection = helpUpdate(query, new String[]{"T3", "T2", "T1"}, t3_match, results);

        BasicDBObject t3_row = new BasicDBObject();
        t3_row.append("e2", 2);
        t3_row.append("e3", 3);

        BasicDBObject t2_match = new BasicDBObject();
        t2_match.append("e1", 1);

        BasicDBObject t1row = new BasicDBObject("T2", results.get(0));
        BasicDBObject t2row = new BasicDBObject("T3", results.get(0));

        BasicDBObject t1_match = new BasicDBObject();
        t1_match.append("e1", 1);

        Mockito.verify(dbCollection, Mockito.never()).insert(t3_row, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(t3_match, new BasicDBObject("$set", t3_row), false, true, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(t2_match, new BasicDBObject("$set", t2row), false, true, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(t1_match, new BasicDBObject("$set", t1row), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    @Ignore
    public void tesNestedEmbeddingUpdateInMiddle() throws Exception {
        String query = "UPDATE T2 SET e2 = 2, e3 = 3 WHERE e1 = 1";

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 1);

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        results.add(new BasicDBObject("_id", 1).append("key", "value"));

        DBCollection dbCollection = helpUpdate(query, new String[]{"T2", "T1", "T3"}, match, results);

        BasicDBObject row = new BasicDBObject();
        row.append("e2", 2);
        row.append("e3", 3);

        BasicDBObject t2_match = new BasicDBObject();
        t2_match.append("e1.$id", 1);

        BasicDBObject t2row = new BasicDBObject("T2", results.get(0));

        Mockito.verify(dbCollection, Mockito.never()).insert(row, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", row), false, true, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(t2_match, new BasicDBObject("$set", t2row), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeInsert() throws Exception {
        String query = "INSERT INTO customer (customer_id, name) VALUES (1, 'jboss')";

        BasicDBObject customer_result = new BasicDBObject();
        customer_result.append("name", "jboss");
        customer_result.append("_id", 1);

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 1);

        DBCollection dbCollection = helpUpdate(query, new String[]{"customer"}, null, null);
        Mockito.verify(dbCollection).insert(customer_result, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.never()).update(new BasicDBObject(), match, false, true, WriteConcern.ACKNOWLEDGED);

        // { "$push" : { "rental" : { "amount" : "3.99" , "customer_id" : { "$ref" : "customer" , "$id" : 1} , "_id" : 1}}},
        BasicDBObject rental = new BasicDBObject();
        rental.append("amount", 3.99);
        //rental.append("customer_id", new DBRef(null, "customer", 1));
        rental.append("_id", 2);
        BasicDBObject rentalresult = new BasicDBObject("rental", rental);

        query = "INSERT INTO rental (rental_id, amount, customer_id) VALUES (2, 3.99, 1)";
        dbCollection = helpUpdate(query, new String[]{"customer"}, customer_result, null);
        Mockito.verify(dbCollection, Mockito.never()).insert(customer_result, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$push", rentalresult), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testArrayInsert() throws Exception {
        String query = "insert into ArrayTest(id,column1) VALUES (1, ('jboss', 'teiid', 'Mr.Lizard'))";

        DBCollection dbCollection = helpUpdate(query, new String[]{"ArrayTest"}, null, null);

        BasicDBList col1 = new BasicDBList();
        col1.add("jboss");
        col1.add("teiid");
        col1.add("Mr.Lizard");

        BasicDBObject result = new BasicDBObject();
        result.append("id", 1);
        result.append("column1", col1);
        Mockito.verify(dbCollection).insert(result, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.never()).update(new BasicDBObject(), result, false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testArrayUpdate() throws Exception {
        String query = "UPDATE ArrayTest SET column1 = ('jboss', 'teiid', 'Mr.Lizard') WHERE id = 1";

        BasicDBObject match = new BasicDBObject("id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"ArrayTest"}, match, null);

        BasicDBList col1 = new BasicDBList();
        col1.add("jboss");
        col1.add("teiid");
        col1.add("Mr.Lizard");

        BasicDBObject details = new BasicDBObject();
        details.append("column1", col1);

        details = new BasicDBObject("$set", details);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, details, false, true, WriteConcern.ACKNOWLEDGED);
    }

    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    // NEW Tests based on N1, N2, N3, N4, N5, N6, N7
    // N1 -> N2 -> N3
    // N1 -> N2 -> N4[]
    // N1 -> N5[] -> N6
    // N1 -> N5[] -> N7[]
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////

    @Test // one to many
    public void testMergeDelete_one_2_many_all() throws Exception {
        String query = "DELETE FROM N5";

        BasicDBObject N5ROW1 = new BasicDBObject().append("e1", 3).append("e3", 3);
        BasicDBObject N5ROW2 = new BasicDBObject().append("e1", 3).append("e3", 3);
        BasicDBList N5Rows = new BasicDBList();
        N5Rows.add(N5ROW1);
        N5Rows.add(N5ROW2);

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        N1ROWS.add(new BasicDBObject("_id", 1).append("N5", N5Rows));

        DBObject match = QueryBuilder.start("N5").exists(true).get();
        DBCollection dbCollection = helpUpdate(query, new String[] { "N1" }, match, N1ROWS);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$pull", new BasicDBObject("N5", new BasicDBObject())),
                false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testMergeDelete_one_2_many_usingFK() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM N5 WHERE e2 = 100";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("e1", 1).append("e3", 5));
        row.add(new BasicDBObject("e1", 2).append("e3", 5));
        results.add(new BasicDBObject("_id", 100).append("N5", row));

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, results);

        BasicDBObject details = new BasicDBObject("N5", new BasicDBList());
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testMergeDelete_one_2_many_usingFK2() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM N5 WHERE e2 = 100 and e1=1";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("_id", 1).append("e3", 5));
        row.add(new BasicDBObject("_id", 2).append("e3", 5));
        results.add(new BasicDBObject("_id", 100).append("N5", row));

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, results);

        BasicDBList expected = new BasicDBList();
        expected.add(new BasicDBObject("_id", 2).append("e3", 5));
        BasicDBObject details = new BasicDBObject("N5", expected);
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeDelete_one_2_many_many() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM N7 WHERE e2 = 5 and e1=7";

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        BasicDBList N5ROW = new BasicDBList();
        N5ROW.add(5);
        N5ROW.add(51);

        BasicDBList N5ROW2 = new BasicDBList();
        N5ROW2.add(50);
        N5ROW2.add(5);

        BasicDBList N5_N7ROW = new BasicDBList();
        N5_N7ROW.add(new BasicDBObject("_id", 7).append("e3", 7));

        BasicDBList N5_N7ROW_ARRAY1 = new BasicDBList();
        N5_N7ROW_ARRAY1.add(N5_N7ROW);

        BasicDBList N5_N7ROW2 = new BasicDBList();
        N5_N7ROW2.add(new BasicDBObject("_id", 5).append("e3", 7));
        N5_N7ROW2.add(new BasicDBObject("_id", 7).append("e3", 7));

        BasicDBList N5_N7ROW_ARRAY2 = new BasicDBList();
        N5_N7ROW_ARRAY2.add(new BasicDBList());
        N5_N7ROW_ARRAY2.add(N5_N7ROW2);

        N1ROWS.add(new BasicDBObject("_id", 1).append("N5", N5ROW).append("N5_N7", N5_N7ROW_ARRAY1));
        N1ROWS.add(new BasicDBObject("_id", 2).append("N5", N5ROW2).append("N5_N7", N5_N7ROW_ARRAY2));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, N1ROWS);

        ArgumentCaptor<BasicDBObject> matchCaptor = ArgumentCaptor.forClass(BasicDBObject.class);
        ArgumentCaptor<BasicDBObject> updateCaptor = ArgumentCaptor.forClass(BasicDBObject.class);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(2)).update(matchCaptor.capture(), updateCaptor.capture(), Mockito.eq(false),
                Mockito.eq(true), Mockito.eq(WriteConcern.ACKNOWLEDGED));

        assertEquals(new BasicDBObject("_id", 1), matchCaptor.getAllValues().get(0));
        assertEquals(new BasicDBObject("_id", 2), matchCaptor.getAllValues().get(1));

        BasicDBList EXPECTED_N5_N7ROW2 = new BasicDBList();
        EXPECTED_N5_N7ROW2.add(new BasicDBObject("_id", 5).append("e3", 7));

        BasicDBList EXPECTED_N5_N7ROW2_ARRAY = new BasicDBList();
        EXPECTED_N5_N7ROW2_ARRAY.add(EXPECTED_N5_N7ROW2);

        assertEquals(new BasicDBObject("$set", new BasicDBObject("N5.0.N7", new BasicDBList())),
                updateCaptor.getAllValues().get(0));

        assertEquals(new BasicDBObject("$set", new BasicDBObject("N5.1.N7", EXPECTED_N5_N7ROW2)),
                updateCaptor.getAllValues().get(1));
    }

    @Test
    public void testMergeDelete_one_2_one_2_many() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM N4 WHERE e2 = 100 and e1=1";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("_id", 1).append("e3", 5));
        row.add(new BasicDBObject("_id", 2).append("e3", 5));
        results.add(new BasicDBObject("_id", 100).append("N2_N4", row));

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, results);

        BasicDBList expected = new BasicDBList();
        expected.add(new BasicDBObject("_id", 2).append("e3", 5));
        BasicDBObject details = new BasicDBObject("N2.N4", expected);
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testMergeDelete_one_2_one_many_onFK() throws Exception {
        // tests one-to-many situation
        String query = "DELETE FROM N4 WHERE e2 = 100";

        ArrayList<DBObject> results = new ArrayList<DBObject>();
        BasicDBList row = new BasicDBList();
        row.add(new BasicDBObject("e1", 1).append("e3", 5));
        row.add(new BasicDBObject("e1", 2).append("e3", 5));
        results.add(new BasicDBObject("_id", 100).append("N2_N4", row));

        BasicDBObject match = new BasicDBObject("_id", 100);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, results);

        BasicDBObject details = new BasicDBObject("N2.N4", new BasicDBList());
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", details), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testUpdate() throws Exception {
        String query = "UPDATE N1 SET e2 = 2, e3 = 3 WHERE e1 = 1";

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, null);

        BasicDBObject details = new BasicDBObject();
        details.append("e2", 2);
        details.append("e3", 3);

        details = new BasicDBObject("$set", details);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, details, false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testMergeUpdate_one_2_one() throws Exception {
        String query = "UPDATE N2 SET e2 = 2 WHERE e2 = 3";

        BasicDBObject N2ROW = new BasicDBObject().append("e2", 3).append("e3", 2);

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        N1ROWS.add(new BasicDBObject("_id", 1).append("N2", N2ROW));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[] { "N1" }, match, N1ROWS);

        BasicDBObject N2UPDATE = new BasicDBObject();
        N2UPDATE.append("N2", new BasicDBObject().append("e2", 2).append("e3", 2));

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", N2UPDATE), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test // one-2-many mapping update merge case;
    public void testMergeUpdate_one_2_many() throws Exception {
        // tests one-to-many situation
        String query = "UPDATE N5 SET e3 = 5 WHERE e1 = 1";

        // { "$project" : { "N5" : "$N5" , "_id" : "$_id"}}

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        BasicDBList N5ROW = new BasicDBList();
        N5ROW.add(new BasicDBObject("_id", 1).append("e2", 1).append("e3", 1));
        N5ROW.add(new BasicDBObject("_id", 2).append("e2", 1).append("e3", 2));
        N1ROWS.add(new BasicDBObject("_id", 1).append("e2", 1).append("e3", 1).append("N5", N5ROW));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, N1ROWS);

        BasicDBList expected = new BasicDBList();
        expected.add(new BasicDBObject("_id", 1).append("e2", 1).append("e3", 5));
        expected.add(new BasicDBObject("_id", 2).append("e2", 1).append("e3", 2));

        BasicDBObject update = new BasicDBObject("N5", expected);
        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", update), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeInsert_one_2_one() throws Exception {
        DBObject n1_match = QueryBuilder.start().and( QueryBuilder.start("N2").exists(true).get(), new BasicDBObject("_id", 1)).get();

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 1);

        BasicDBObject n3 = new BasicDBObject();
        n3.append("e2", 3);
        n3.append("e3", 3);

        BasicDBObject result = new BasicDBObject("N2.N3", n3);

        String query = "INSERT INTO N3 (e1, e2, e3) VALUES (1,3,3)";
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, null);
        Mockito.verify(dbCollection, Mockito.never()).insert(n1_match, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(n1_match, new BasicDBObject("$set", result), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeUpdate_one_2_one_2_one() throws Exception {
        String query = "UPDATE N3 SET e2 = 2 WHERE e1 = 1 and e2 = 3";

        //{ "$project" : { "N2_N3" : "$N2.N3" , "N2" : "$N2._id" , "_id" : "$_id"}}

        BasicDBObject N3ROW = new BasicDBObject().append("e2", 3).append("e3", 3);

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        N1ROWS.add(new BasicDBObject("_id", 1).append("N2_N3", N3ROW));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[] { "N1" }, match, N1ROWS);

        BasicDBObject N3UPDATE = new BasicDBObject();
        N3UPDATE.append("N2.N3", new BasicDBObject().append("e2", 2).append("e3", 3));

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", N3UPDATE), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeDelete_one_2_one_2_one() throws Exception {
        String query = "DELETE FROM N3 WHERE e1 = 1 and e2 = 3";

        //{ "$project" : { "N2_N3" : "$N2.N3" , "N2" : "$N2._id" , "_id" : "$_id"}}

        BasicDBObject N3ROW = new BasicDBObject().append("e2", 3).append("e3", 3);

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        N1ROWS.add(new BasicDBObject("_id", 1).append("N2_N3", N3ROW));

        DBObject match = QueryBuilder.start().and(new BasicDBObject("_id", 1), new BasicDBObject("N2.N3.e2", 3)).get();
        DBCollection dbCollection = helpUpdate(query, new String[] { "N1" }, match, N1ROWS);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$unset", new BasicDBObject("N2.N3", "")), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeUpdate_one_2_one_2_many() throws Exception {
        String query = "UPDATE N4 SET e3 = 5 WHERE e1 = 1";

        BasicDBList N4ROW = new BasicDBList();
        N4ROW.add(new BasicDBObject("_id", 1).append("e3", 1));
        N4ROW.add(new BasicDBObject("_id", 2).append("e3", 2));

        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        N1ROWS.add(new BasicDBObject("_id", 1).append("N2_N4", N4ROW));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[] { "N1" }, match, N1ROWS);

        BasicDBList expected = new BasicDBList();
        expected.add(new BasicDBObject("_id", 1).append("e3", 5));
        expected.add(new BasicDBObject("_id", 2).append("e3", 2));

        BasicDBObject N4UPDATE = new BasicDBObject("N2.N4", expected);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(match, new BasicDBObject("$set", N4UPDATE), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeUpdate_one_2_many_2_one() throws Exception {
        String query = "UPDATE N6 SET e3 = 5 WHERE e1 = 5";

        // { "$project" : { "N5_N6" : "$N5.N6" , "N5" : "$N5._id" , "_id" : "$_id"}}
        // { "_id" : 1, "N5" : [ 5, 51 ], "N5_N6" : [ { "e2" : 6, "e3" : 6 }, { "e2" : 0, "e3" : 6 } ] }
        // N5.e1 = N6.e1
        // N5.e2 = N1.e1
        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        BasicDBList N5ROW = new BasicDBList();
        N5ROW.add(5);
        N5ROW.add(51);

        BasicDBList N5ROW2 = new BasicDBList();
        N5ROW2.add(50);
        N5ROW2.add(5);

        BasicDBList N5_N6ROW = new BasicDBList();
        N5_N6ROW.add(new BasicDBObject("e2", 6).append("e3", 6));
        N5_N6ROW.add(new BasicDBObject("e2", 0).append("e3", 6));

        BasicDBList N5_N6ROW2 = new BasicDBList();
        N5_N6ROW2.add(new BasicDBObject("e2", 1).append("e3", 1));
        N5_N6ROW2.add(new BasicDBObject("e2", 2).append("e3", 2));

        N1ROWS.add(new BasicDBObject("_id", 1).append("N5", N5ROW).append("N5_N6", N5_N6ROW));
        N1ROWS.add(new BasicDBObject("_id", 2).append("N5", N5ROW2).append("N5_N6", N5_N6ROW2));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, N1ROWS);

        // one from each of the N5_N6 array is updated.
        BasicDBObject expected1 = new BasicDBObject("e2", 6).append("e3", 5);
        BasicDBObject expected2 = new BasicDBObject("e2", 2).append("e3", 5);

        ArgumentCaptor<BasicDBObject> matchCaptor = ArgumentCaptor.forClass(BasicDBObject.class);
        ArgumentCaptor<BasicDBObject> updateCaptor = ArgumentCaptor.forClass(BasicDBObject.class);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(2)).update(matchCaptor.capture(), updateCaptor.capture(), Mockito.eq(false),
                Mockito.eq(true), Mockito.eq(WriteConcern.ACKNOWLEDGED));

        assertEquals(new BasicDBObject("_id", 1), matchCaptor.getAllValues().get(0));
        assertEquals(new BasicDBObject("_id", 2), matchCaptor.getAllValues().get(1));

        assertEquals(new BasicDBObject("$set", new BasicDBObject("N5.0.N6", expected1)),
                updateCaptor.getAllValues().get(0));
        assertEquals(new BasicDBObject("$set", new BasicDBObject("N5.1.N6", expected2)),
                updateCaptor.getAllValues().get(1));
    }

    @Test
    public void testNestedMergeUpdate_one_2_many_2_many() throws Exception {
        String query = "UPDATE N7 SET e3 = 5 WHERE e1 = 5";

        // { "$project" : { "N5_N7" : "$N5.N7" , "N5" : "$N5._id" , "_id" : "$_id"}}
        // { "_id" : 1, "N5" : [ 5, 51 ], "N5_N7" : [ [ { "e3" : 7, "_id" : 7 } ]] ] }
        // { "_id" : 1, "N5" : [ 50, 5 ], "N5_N7" : [ [ { "e3" : 7, "_id" : 7 } ], [ { "e3" : 7, "_id" : 5 }, { "e3" : 7, "_id" : 7 } ] ] }
        // N5.e1 = N7.e2
        // N5.e2 = N1.e1
        ArrayList<DBObject> N1ROWS = new ArrayList<DBObject>();
        BasicDBList N5ROW = new BasicDBList();
        N5ROW.add(5);
        N5ROW.add(51);

        BasicDBList N5ROW2 = new BasicDBList();
        N5ROW2.add(50);
        N5ROW2.add(5);

        BasicDBList N5_N7ROW = new BasicDBList();
        N5_N7ROW.add(new BasicDBObject("_id", 7).append("e3", 7));

        BasicDBList N5_N7ROW_ARRAY1 = new BasicDBList();
        N5_N7ROW_ARRAY1.add(N5_N7ROW);

        BasicDBList N5_N7ROW2 = new BasicDBList();
        N5_N7ROW2.add(new BasicDBObject("_id", 5).append("e3", 7));
        N5_N7ROW2.add(new BasicDBObject("_id", 7).append("e3", 7));

        BasicDBList N5_N7ROW_ARRAY2 = new BasicDBList();
        N5_N7ROW_ARRAY2.add(new BasicDBList());
        N5_N7ROW_ARRAY2.add(N5_N7ROW2);

        N1ROWS.add(new BasicDBObject("_id", 1).append("N5", N5ROW).append("N5_N7", N5_N7ROW_ARRAY1));
        N1ROWS.add(new BasicDBObject("_id", 2).append("N5", N5ROW2).append("N5_N7", N5_N7ROW_ARRAY2));

        BasicDBObject match = new BasicDBObject("_id", 1);
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, N1ROWS);

        ArgumentCaptor<BasicDBObject> matchCaptor = ArgumentCaptor.forClass(BasicDBObject.class);
        ArgumentCaptor<BasicDBObject> updateCaptor = ArgumentCaptor.forClass(BasicDBObject.class);

        Mockito.verify(dbCollection, Mockito.never()).insert(new BasicDBObject(), WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection, Mockito.times(1)).update(matchCaptor.capture(), updateCaptor.capture(), Mockito.eq(false),
                Mockito.eq(true), Mockito.eq(WriteConcern.ACKNOWLEDGED));

        assertEquals(new BasicDBObject("_id", 2), matchCaptor.getAllValues().get(0));

        BasicDBList EXPECTED_N5_N7ROW2 = new BasicDBList();
        EXPECTED_N5_N7ROW2.add(new BasicDBObject("_id", 5).append("e3", 5));
        EXPECTED_N5_N7ROW2.add(new BasicDBObject("_id", 7).append("e3", 7));

        BasicDBList EXPECTED_N5_N7ROW2_ARRAY = new BasicDBList();
        EXPECTED_N5_N7ROW2_ARRAY.add(EXPECTED_N5_N7ROW2);

        assertEquals(new BasicDBObject("$set", new BasicDBObject("N5.1.N7", EXPECTED_N5_N7ROW2)),
                updateCaptor.getAllValues().get(0));
    }

    @Test
    public void testNestedMergeInsert_One_2_One_Many() throws Exception {
        DBObject n1_match = QueryBuilder.start().and(QueryBuilder.start("N2").exists(true).get(),
                new BasicDBObject("_id", 2)).get();

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 3);

        BasicDBObject n4 = new BasicDBObject();
        n4.append("e3", 3);
        n4.append("_id",1);

        BasicDBObject result = new BasicDBObject("N2.N4", n4);

        String query = "INSERT INTO N4 (e1, e2, e3) VALUES (1,2,3)";
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, null);
        Mockito.verify(dbCollection, Mockito.never()).insert(n1_match, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(n1_match, new BasicDBObject("$push", result), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeInsert_One_2_Many_one() throws Exception {
        DBObject n1_match = QueryBuilder.start().and(QueryBuilder.start("N5").exists(true).get(),
                new BasicDBObject("N5._id", 1)).get();

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 3);

        BasicDBObject n6 = new BasicDBObject();
        n6.append("e2", 2);
        n6.append("e3", 3);

        BasicDBObject result = new BasicDBObject("N5.$.N6", n6);

        String query = "INSERT INTO N6 (e1, e2, e3) VALUES (1,2,3)";
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, null);
        Mockito.verify(dbCollection, Mockito.never()).insert(n1_match, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(n1_match, new BasicDBObject("$set", result), false, true, WriteConcern.ACKNOWLEDGED);
    }

    @Test
    public void testNestedMergeInsert_One_2_Many_Many() throws Exception {
        DBObject n1_match = QueryBuilder.start().and(QueryBuilder.start("N5").exists(true).get(),
                new BasicDBObject("N5._id", 2)).get();

        BasicDBObject match = new BasicDBObject();
        match.append("_id", 3);

        BasicDBObject n7 = new BasicDBObject();
        n7.append("e3", 3);
        n7.append("_id",1);

        BasicDBObject result = new BasicDBObject("N5.$.N7", n7);

        String query = "INSERT INTO N7 (e1, e2, e3) VALUES (1,2,3)";
        DBCollection dbCollection = helpUpdate(query, new String[]{"N1"}, match, null);
        Mockito.verify(dbCollection, Mockito.never()).insert(n1_match, WriteConcern.ACKNOWLEDGED);
        Mockito.verify(dbCollection).update(n1_match, new BasicDBObject("$push", result), false, true, WriteConcern.ACKNOWLEDGED);
    }
}

{'repo_name': 'teiid/teiid', 'stars': '208', 'repo_language': 'Java', 'file_name': 'TestOptionalGeospatial.java', 'mime_type': 'text/x-java', 'hash': -6719115240878476475, 'source_dataset': 'data'}


// @flow
// Generates a graphql schema from a type definition AST

import path from 'path';
import fs from 'fs';
import camel from 'camel-case';
import {parse, buildASTSchema, concatAST, printSchema, GraphQLObjectType,
  getNamedType, GraphQLSchema} from 'graphql';
import {mutationWithClientMutationId} from 'graphql-relay';
import {insertConnectionTypes, removeHiddenNodes} from './ASTTransforms';
import databaseInfoFromAST from './databaseInfoFromAST';
import scalarTypeDefinitions from './scalarTypeDefinitions';
import baseSchema from './baseSchema';
import type {Document, ObjectTypeDefinition, GraphQLField,
  GraphQLFieldResolveFn, MutationDefinitionFn, MutationDefinition,
  DatabaseInterface, DatabaseInterfaceDefinitionFn,
  ObjectTypeFieldResolutionDefinition, GraphQLType, TypeMap, Relationship,
  GestaltServerConfig} from 'gestalt-utils';

// generate a graphql schema given object types,
export default function generateGraphQLSchema(
  schemaText: string,
  objects: ObjectTypeFieldResolutionDefinition[],
  mutations: MutationDefinitionFn[],
  databaseInterfaceDefinitionFn: DatabaseInterfaceDefinitionFn,
  config?: GestaltServerConfig,
): {schema: GraphQLSchema, databaseInterface: DatabaseInterface} {
  const ast = parse(schemaText);

  // we take inventory of object definitions and relationships before the ast
  // is modified
  const {objectDefinitions, relationships} = databaseInfoFromAST(ast);
  const databaseInterface = databaseInterfaceDefinitionFn(
    objectDefinitions,
    relationships,
    config,
  );

  // this modifies the AST by prepending the base schema, removing hidden
  // fields, and inserting connection types
  const schema = generateGraphQLSchemaWithoutResolution(ast, mutations);

  // log generated schema
  // console.log(printSchema(schema));

  defineScalarTypes(schema);
  defineBaseSchemaResolution(schema, databaseInterface);
  defineRelationshipResolution(schema, relationships, databaseInterface);
  defineNodeIDResolution(schema);
  attachObjectTypeFieldResolution(schema, objects);

  return {schema, databaseInterface};
}

export function generateGraphQLSchemaWithoutResolution(
  ast: Document,
  mutations: MutationDefinitionFn[]
): GraphQLSchema {
  // preprocess the AST to remove hidden types and insert implied types
  const modifiedAST = concatAST([baseSchema, ast]);
  removeHiddenNodes(modifiedAST);
  insertConnectionTypes(modifiedAST);

  // generate GraphQLSchema
  const schema = buildASTSchema(modifiedAST);

  // create and attach mutations
  defineMutations(schema, mutations);

  return schema;
}

// attach serialization and parsing functions to scalar types defined by
// baseSchema.graphql
function defineScalarTypes(schema: GraphQLSchema): void {
  Object.entries(scalarTypeDefinitions).forEach(([typeName, definition]) => {
    const type = schema.getType(typeName);
    Object.assign(type._scalarConfig, definition);
  });
}

// define field resolution on object types defined by baseSchema.graphql
function defineBaseSchemaResolution(
  schema: GraphQLSchema,
  database: DatabaseInterface,
): void {
  defineFieldResolve(schema, 'QueryRoot', 'node', database.resolveNode);
  defineFieldResolve(schema, 'QueryRoot', 'session', resolveSession);
  defineFieldResolve(schema, 'Session', 'id', () => 'Session:!');
}

// generate resolve functions for connection fields
function defineRelationshipResolution(
  schema: GraphQLSchema,
  relationships: Relationship[],
  database: DatabaseInterface,
): void {
  relationships.forEach(relationship => {
    defineFieldResolve(
      schema,
      relationship.path[0].fromType,
      relationship.fieldName,
      database.generateRelationshipResolver(relationship),
    );
  });
}

// we define id resolution to include both a type name and database uuid
function defineNodeIDResolution(schema: GraphQLSchema): void {
  const typeMap = schema.getTypeMap();
  const nodeInterface = typeMap.Node;

  nodeInterface.resolveType = obj => typeMap[obj._type];

  Object.values(typeMap).forEach((type: GraphQLType): void => {
    if (
      type.getInterfaces &&
      type.getInterfaces().includes(nodeInterface) &&
      type.name !== 'Session'
    ) {
      defineFieldResolve(
        schema,
        type.name,
        'id',
        obj => `${type.name}:${obj.id}`,
      );
    }
  });
}

// attach user defined field resolution
function attachObjectTypeFieldResolution(
  schema: GraphQLSchema,
  objects: ObjectTypeFieldResolutionDefinition[],
): void {
  objects.forEach(objectType => {
    const typeName = objectType.name;
    Object.entries(objectType.fields).forEach(([fieldName, resolve]) => {
      defineFieldResolve(schema, typeName, fieldName, resolve);
    });
  });
}

// attach user defined mutations to schema
function defineMutations(
  schema: GraphQLSchema,
  mutations: MutationDefinitionFn[],
): void {
  if (mutations.length === 0) {
    return;
  }

  const types = schema.getTypeMap();
  const MutationRoot = new GraphQLObjectType({
    name: 'MutationRoot',
    fields: mutations.reduce((memo, mutationDefinition) => {
      const definition = transformSimpleMutationDefinition(
        mutationDefinition(types)
      );
      memo[camel(definition.name)] = mutationWithClientMutationId(definition);
      return memo;
    }, {}),
  });

  schema._mutationType = MutationRoot;
  const newTypes = newTypesFromMutationType(MutationRoot);

  extendTypeMap(types, newTypesFromMutationType(MutationRoot));
}

// allow GraphQLTypes to be provided in place of InputObjectFieldConfigs or
// GraphQLFieldConfigs as part of the mutation config.  If a GraphQLType is
// provided directly, it implies {type: GraphQLType} and leavs all other options
// blank.
function transformSimpleMutationDefinition(
  definition: MutationDefinition,
): MutationDefinition {
  Object.keys(definition.inputFields).forEach(key => {
    const field = definition.inputFields[key];
    if (field.type == null) {
      definition.inputFields[key] = {type: field};
    }
  });
  Object.keys(definition.outputFields).forEach(key => {
    const field = definition.outputFields[key];
    if (field.type == null) {
      definition.outputFields[key] = {type: field};
    }
  });
  return definition;
}

function extendTypeMap(
  typeMap: TypeMap,
  types: GraphQLType[],
): void {
  types.forEach(type => {
    if (typeMap[type.name]) {
      if (typeMap[type.name] !== type) {
        throw new Error(`Duplicate definition of '${type.name}'`);
      }
      return;
    }

    typeMap[type.name] = type;
  });
}

function newTypesFromMutationType(
  mutationType: GraphQLObjectType
): GraphQLType[] {
  const fields: GraphQLField[] = Object.values(mutationType.getFields());

  return fields.reduce((memo, field) =>
    memo.concat(
      field.type,
      getNamedType(field.args[0].type),
    )
  , [mutationType]);
}

function defineFieldResolve(
  schema: GraphQLSchema,
  typeName: string,
  fieldName: string,
  resolve: GraphQLFieldResolveFn,
): void {
  const type = schema.getType(typeName);
  const field = type.getFields()[fieldName];
  field.resolve = resolve;
}

function resolveSession(
  object: Object,
  args: Object,
  context: Object
): Object {
  return context.session;
}

{'repo_name': 'charlieschwabacher/gestalt', 'stars': '204', 'repo_language': 'JavaScript', 'file_name': 'mocha.opts', 'mime_type': 'text/plain', 'hash': 5537732943503937004, 'source_dataset': 'data'}


/*!
 * froala_editor v2.8.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2018 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            return factory(jQuery);
        };
    } else {
        // Browser globals
        factory(window.jQuery);
    }
}(function ($) {
/**
 * Traditional Chinese spoken in Taiwan.
 */

$.FE.LANGUAGE['zh_tw'] = {
  translation: {
    // Place holder
    "Type something": "\u8f38\u5165\u4e00\u4e9b\u5167\u5bb9",

    // Basic formatting
    "Bold": "\u7c97\u9ad4",
    "Italic": "\u659c\u9ad4",
    "Underline": "\u5e95\u7dda",
    "Strikethrough": "\u522a\u9664\u7dda",

    // Main buttons
    "Insert": "\u63d2\u5165",
    "Delete": "\u522a\u9664",
    "Cancel": "\u53d6\u6d88",
    "OK": "\u78ba\u5b9a",
    "Back": "\u5f8c",
    "Remove": "\u79fb\u9664",
    "More": "\u66f4\u591a",
    "Update": "\u66f4\u65b0",
    "Style": "\u6a23\u5f0f",

    // Font
    "Font Family": "\u5b57\u9ad4",
    "Font Size": "\u5b57\u578b\u5927\u5c0f",

    // Colors
    "Colors": "\u984f\u8272",
    "Background": "\u80cc\u666f",
    "Text": "\u6587\u5b57",
    "HEX Color": "十六進制顏色",

    // Paragraphs
    "Paragraph Format": "\u683c\u5f0f",
    "Normal": "\u6b63\u5e38",
    "Code": "\u7a0b\u5f0f\u78bc",
    "Heading 1": "\u6a19\u984c 1",
    "Heading 2": "\u6a19\u984c 2",
    "Heading 3": "\u6a19\u984c 3",
    "Heading 4": "\u6a19\u984c 4",

    // Style
    "Paragraph Style": "\u6bb5\u843d\u6a23\u5f0f",
    "Inline Style": "\u5167\u806f\u6a23\u5f0f",

    // Alignment
    "Align": "\u5c0d\u9f4a",
    "Align Left": "\u7f6e\u5de6\u5c0d\u9f4a",
    "Align Center": "\u7f6e\u4e2d\u5c0d\u9f4a",
    "Align Right": "\u7f6e\u53f3\u5c0d\u9f4a",
    "Align Justify": "\u5de6\u53f3\u5c0d\u9f4a",
    "None": "\u7121",

    // Lists
    "Ordered List": "\u6578\u5b57\u6e05\u55ae",
    "Unordered List": "\u9805\u76ee\u6e05\u55ae",

    // Indent
    "Decrease Indent": "\u6e1b\u5c11\u7e2e\u6392",
    "Increase Indent": "\u589e\u52a0\u7e2e\u6392",

    // Links
    "Insert Link": "\u63d2\u5165\u9023\u7d50",
    "Open in new tab": "\u5728\u65b0\u5206\u9801\u958b\u555f",
    "Open Link": "\u958b\u555f\u9023\u7d50",
    "Edit Link": "\u7de8\u8f2f\u9023\u7d50",
    "Unlink": "\u79fb\u9664\u9023\u7d50",
    "Choose Link": "\u9078\u64c7\u9023\u7d50",

    // Images
    "Insert Image": "\u63d2\u5165\u5716\u7247",
    "Upload Image": "\u4e0a\u50b3\u5716\u7247",
    "By URL": "\u7db2\u5740\u4e0a\u50b3",
    "Browse": "\u700f\u89bd",
    "Drop image": "\u5716\u7247\u62d6\u66f3",
    "or click": "\u6216\u9ede\u64ca",
    "Manage Images": "\u7ba1\u7406\u5716\u7247",
    "Loading": "\u8f09\u5165\u4e2d",
    "Deleting": "\u522a\u9664",
    "Tags": "\u6a19\u7c64",
    "Are you sure? Image will be deleted.": "\u78ba\u5b9a\u522a\u9664\u5716\u7247\uff1f",
    "Replace": "\u66f4\u63db",
    "Uploading": "\u4e0a\u50b3",
    "Loading image": "\u4e0a\u50b3\u4e2d",
    "Display": "\u986f\u793a",
    "Inline": "\u5d4c\u5165",
    "Break Text": "\u8207\u6587\u5b57\u5206\u96e2",
    "Alternative Text": "\u6587\u5b57\u74b0\u7e5e",
    "Change Size": "\u8abf\u6574\u5927\u5c0f",
    "Width": "\u5bec\u5ea6",
    "Height": "\u9ad8\u5ea6",
    "Something went wrong. Please try again.": "\u932f\u8aa4\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002",
    "Image Caption": "圖片說明",
    "Advanced Edit": "高級編輯",

    // Video
    "Insert Video": "\u63d2\u5165\u5f71\u7247",
    "Embedded Code": "\u5d4c\u5165\u7a0b\u5f0f\u78bc",
    "Paste in a video URL": "粘貼在視頻網址",
    "Drop video": "放下視頻",
    "Your browser does not support HTML5 video.": "您的瀏覽器不支持html5視頻。",
    "Upload Video": "上傳視頻",

    // Tables
    "Insert Table": "\u63d2\u5165\u8868\u683c",
    "Table Header": "\u8868\u982d",
    "Remove Table": "\u522a\u9664\u8868",
    "Table Style": "\u8868\u6a23\u5f0f",
    "Horizontal Align": "\u6c34\u6e96\u5c0d\u9f4a\u65b9\u5f0f",
    "Row": "\u884c",
    "Insert row above": "\u5411\u4e0a\u63d2\u5165\u4e00\u884c",
    "Insert row below": "\u5411\u4e0b\u63d2\u5165\u4e00\u884c",
    "Delete row": "\u522a\u9664\u884c",
    "Column": "\u5217",
    "Insert column before": "\u5411\u5de6\u63d2\u5165\u4e00\u5217",
    "Insert column after": "\u5411\u53f3\u63d2\u5165\u4e00\u5217",
    "Delete column": "\u522a\u9664\u884c",
    "Cell": "\u5132\u5b58\u683c",
    "Merge cells": "\u5408\u4f75\u5132\u5b58\u683c",
    "Horizontal split": "\u6c34\u5e73\u5206\u5272",
    "Vertical split": "\u5782\u76f4\u5206\u5272",
    "Cell Background": "\u5132\u5b58\u683c\u80cc\u666f",
    "Vertical Align": "\u5782\u76f4\u5c0d\u9f4a\u65b9\u5f0f",
    "Top": "\u4e0a",
    "Middle": "\u4e2d",
    "Bottom": "\u4e0b",
    "Align Top": "\u5411\u4e0a\u5c0d\u9f4a",
    "Align Middle": "\u4e2d\u9593\u5c0d\u9f4a",
    "Align Bottom": "\u5e95\u90e8\u5c0d\u9f4a",
    "Cell Style": "\u5132\u5b58\u683c\u6a23\u5f0f",

    // Files
    "Upload File": "\u4e0a\u50b3\u6587\u4ef6",
    "Drop file": "\u6587\u4ef6\u62d6\u66f3",

    // Emoticons
    "Emoticons": "\u8868\u60c5",
    "Grinning face": "\u81c9\u4e0a\u7b11\u563b\u563b",
    "Grinning face with smiling eyes": "\u7b11\u563b\u563b\u7684\u81c9\uff0c\u542b\u7b11\u7684\u773c\u775b",
    "Face with tears of joy": "\u81c9\u4e0a\u5e36\u8457\u559c\u6085\u7684\u6dda\u6c34",
    "Smiling face with open mouth": "\u7b11\u81c9\u5f35\u958b\u5634",
    "Smiling face with open mouth and smiling eyes": "\u7b11\u81c9\u5f35\u958b\u5634\u5fae\u7b11\u7684\u773c\u775b",
    "Smiling face with open mouth and cold sweat": "\u7b11\u81c9\u5f35\u958b\u5634\uff0c\u4e00\u8eab\u51b7\u6c57",
    "Smiling face with open mouth and tightly-closed eyes": "\u7b11\u81c9\u5f35\u958b\u5634\uff0c\u7dca\u7dca\u9589\u8457\u773c\u775b",
    "Smiling face with halo": "\u7b11\u81c9\u6688",
    "Smiling face with horns": "\u5fae\u7b11\u7684\u81c9\u89d2",
    "Winking face": "\u7728\u773c\u8868\u60c5",
    "Smiling face with smiling eyes": "\u9762\u5e36\u5fae\u7b11\u7684\u773c\u775b",
    "Face savoring delicious food": "\u9762\u5c0d\u54c1\u5690\u7f8e\u5473\u7684\u98df\u7269",
    "Relieved face": "\u9762\u5c0d\u5982\u91cb\u91cd\u8ca0",
    "Smiling face with heart-shaped eyes": "\u5fae\u7b11\u7684\u81c9\uff0c\u5fc3\u81df\u5f62\u7684\u773c\u775b",
    "Smiling face with sunglasses": "\u7b11\u81c9\u592a\u967d\u93e1",
    "Smirking face": "\u9762\u5c0d\u9762\u5e36\u7b11\u5bb9",
    "Neutral face": "\u4e2d\u6027\u9762",
    "Expressionless face": "\u9762\u7121\u8868\u60c5",
    "Unamused face": "\u4e00\u81c9\u4e0d\u5feb\u7684\u81c9",
    "Face with cold sweat": "\u9762\u5c0d\u51b7\u6c57",
    "Pensive face": "\u6c89\u601d\u7684\u81c9",
    "Confused face": "\u9762\u5c0d\u56f0\u60d1",
    "Confounded face": "\u8a72\u6b7b\u7684\u81c9",
    "Kissing face": "\u9762\u5c0d\u63a5\u543b",
    "Face throwing a kiss": "\u9762\u5c0d\u6295\u64f2\u4e00\u500b\u543b",
    "Kissing face with smiling eyes": "\u63a5\u543b\u81c9\uff0c\u542b\u7b11\u7684\u773c\u775b",
    "Kissing face with closed eyes": "\u63a5\u543b\u7684\u81c9\u9589\u8457\u773c\u775b",
    "Face with stuck out tongue": "\u9762\u5c0d\u4f38\u51fa\u820c\u982d",
    "Face with stuck out tongue and winking eye": "\u9762\u5c0d\u4f38\u51fa\u820c\u982d\u548c\u7728\u52d5\u7684\u773c\u775b",
    "Face with stuck out tongue and tightly-closed eyes": "\u9762\u5c0d\u4f38\u51fa\u820c\u982d\u548c\u7dca\u9589\u7684\u773c\u775b",
    "Disappointed face": "\u9762\u5c0d\u5931\u671b",
    "Worried face": "\u9762\u5c0d\u64d4\u5fc3",
    "Angry face": "\u61a4\u6012\u7684\u81c9",
    "Pouting face": "\u9762\u5c0d\u5658\u5634",
    "Crying face": "\u54ed\u6ce3\u7684\u81c9",
    "Persevering face": "\u600e\u5948\u81c9",
    "Face with look of triumph": "\u9762\u5e36\u770b\u7684\u52dd\u5229",
    "Disappointed but relieved face": "\u5931\u671b\uff0c\u4f46\u81c9\u4e0a\u91cb\u7136",
    "Frowning face with open mouth": "\u9762\u5c0d\u76ba\u8457\u7709\u982d\u5f35\u53e3",
    "Anguished face": "\u9762\u5c0d\u75db\u82e6",
    "Fearful face": "\u53ef\u6015\u7684\u81c9",
    "Weary face": "\u9762\u5c0d\u53ad\u5026",
    "Sleepy face": "\u9762\u5c0d\u56f0",
    "Tired face": "\u75b2\u618a\u7684\u81c9",
    "Grimacing face": "\u7319\u7370\u7684\u81c9",
    "Loudly crying face": "\u5927\u8072\u54ed\u81c9",
    "Face with open mouth": "\u9762\u5c0d\u5f35\u958b\u5634",
    "Hushed face": "\u5b89\u975c\u7684\u81c9",
    "Face with open mouth and cold sweat": "\u9762\u5c0d\u5f35\u958b\u5634\uff0c\u4e00\u8eab\u51b7\u6c57",
    "Face screaming in fear": "\u9762\u5c0d\u5c16\u53eb\u5728\u6050\u61fc\u4e2d",
    "Astonished face": "\u9762\u5c0d\u9a5a\u8a1d",
    "Flushed face": "\u7d05\u64b2\u64b2\u7684\u81c9\u86cb",
    "Sleeping face": "\u719f\u7761\u7684\u81c9",
    "Dizzy face": "\u9762\u5c0d\u7729",
    "Face without mouth": "\u81c9\u4e0a\u6c92\u6709\u5634",
    "Face with medical mask": "\u9762\u5c0d\u91ab\u7642\u53e3\u7f69",

    // Line breaker
    "Break": "\u63db\u884c",

    // Math
    "Subscript": "\u4e0b\u6a19",
    "Superscript": "\u4e0a\u6a19",

    // Full screen
    "Fullscreen": "\u5168\u87a2\u5e55",

    // Horizontal line
    "Insert Horizontal Line": "\u63d2\u5165\u6c34\u5e73\u7dda",

    // Clear formatting
    "Clear Formatting": "\u6e05\u9664\u683c\u5f0f",

    // Undo, redo
    "Undo": "\u5fa9\u539f",
    "Redo": "\u53d6\u6d88\u5fa9\u539f",

    // Select all
    "Select All": "\u5168\u9078",

    // Code view
    "Code View": "\u539f\u59cb\u78bc",

    // Quote
    "Quote": "\u5f15\u6587",
    "Increase": "\u7e2e\u6392",
    "Decrease": "\u53bb\u9664\u7e2e\u6392",

    // Quick Insert
    "Quick Insert": "\u5feb\u63d2",

    // Spcial Characters
    "Special Characters": "特殊字符",
    "Latin": "拉丁",
    "Greek": "希臘語",
    "Cyrillic": "西里爾",
    "Punctuation": "標點",
    "Currency": "貨幣",
    "Arrows": "箭頭",
    "Math": "數學",
    "Misc": "雜項",

    // Print.
    "Print": "打印",

    // Spell Checker.
    "Spell Checker": "拼寫檢查器",

    // Help
    "Help": "幫幫我",
    "Shortcuts": "快捷鍵",
    "Inline Editor": "內聯編輯器",
    "Show the editor": "顯示編輯",
    "Common actions": "共同行動",
    "Copy": "複製",
    "Cut": "切",
    "Paste": "糊",
    "Basic Formatting": "基本格式",
    "Increase quote level": "提高報價水平",
    "Decrease quote level": "降低報價水平",
    "Image / Video": "圖像/視頻",
    "Resize larger": "調整大小更大",
    "Resize smaller": "調整大小更小",
    "Table": "表",
    "Select table cell": "選擇表單元格",
    "Extend selection one cell": "擴展選擇一個單元格",
    "Extend selection one row": "擴展選擇一行",
    "Navigation": "導航",
    "Focus popup / toolbar": "焦點彈出/工具欄",
    "Return focus to previous position": "將焦點返回到上一個位置",

    // Embed.ly
    "Embed URL": "嵌入網址",
    "Paste in a URL to embed": "粘貼在一個網址中嵌入",

    // Word Paste.
    "The pasted content is coming from a Microsoft Word document. Do you want to keep the format or clean it up?": "粘貼的內容來自微軟Word文檔。你想保留格式還是清理它？",
    "Keep": "保持",
    "Clean": "清潔",
    "Word Paste Detected": "檢測到字貼"
  },
  direction: "ltr"
};

}));

{'repo_name': 'unaio/una', 'stars': '147', 'repo_language': 'PHP', 'file_name': 'ru.xml', 'mime_type': 'text/xml', 'hash': 4056372078182415332, 'source_dataset': 'data'}


// http://math.stackexchange.com/questions/28043/finding-the-z-value-on-a-plane-with-x-y-values
// http://stackoverflow.com/a/13916669/461015
import test from 'tape';

import { polygon } from '@turf/helpers';
import planepoint from './dist/js/index.js';

test('turf-planepoint', t => {
    const point = [1, 1];
    const triangleProps = polygon([[[0, 0], [2, 0], [1, 2], [0, 0]]], {a: 0, b: 0, c: 2});
    const triangleZ = polygon([[[0, 0, 0], [2, 0, 0], [1, 2, 2], [0, 0, 0]]]);

    t.equal(planepoint(point, triangleProps), 1, 'properties');
    t.equal(planepoint(point, triangleZ), 1, 'z coordinates');
    t.end();
});

{'repo_name': 'Turfjs/turf', 'stars': '5381', 'repo_language': 'JavaScript', 'file_name': '5.2.0.md', 'mime_type': 'text/plain', 'hash': 5996293605224534076, 'source_dataset': 'data'}


/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#if !defined(BOOST_SPIRIT_X3_CHAR_CLASS_APRIL_16_2006_1051AM)
#define BOOST_SPIRIT_X3_CHAR_CLASS_APRIL_16_2006_1051AM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/x3/char/char_parser.hpp>
#include <boost/spirit/home/x3/char/detail/cast_char.hpp>
#include <boost/spirit/home/support/char_encoding/standard.hpp>
#include <boost/spirit/home/support/char_encoding/standard_wide.hpp>
#include <boost/spirit/home/support/char_encoding/ascii.hpp>
#include <boost/spirit/home/support/char_encoding/iso8859_1.hpp>

namespace boost { namespace spirit { namespace x3
{
    ///////////////////////////////////////////////////////////////////////////
    struct char_tag {};
    struct alnum_tag {};
    struct alpha_tag {};
    struct blank_tag {};
    struct cntrl_tag {};
    struct digit_tag {};
    struct graph_tag {};
    struct print_tag {};
    struct punct_tag {};
    struct space_tag {};
    struct xdigit_tag {};
    struct lower_tag {};
    struct upper_tag {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Encoding>
    struct char_class_base
    {
        typedef typename Encoding::char_type char_type;

#define BOOST_SPIRIT_X3_CLASSIFY(name)                                             \
        template <typename Char>                                                \
        static bool                                                             \
        is(name##_tag, Char ch)                                                 \
        {                                                                       \
            return Encoding::is##name                                           \
                BOOST_PREVENT_MACRO_SUBSTITUTION                                \
                    (detail::cast_char<char_type>(ch));                         \
        }                                                                       \
        /***/

        BOOST_SPIRIT_X3_CLASSIFY(char)
        BOOST_SPIRIT_X3_CLASSIFY(alnum)
        BOOST_SPIRIT_X3_CLASSIFY(alpha)
        BOOST_SPIRIT_X3_CLASSIFY(digit)
        BOOST_SPIRIT_X3_CLASSIFY(xdigit)
        BOOST_SPIRIT_X3_CLASSIFY(cntrl)
        BOOST_SPIRIT_X3_CLASSIFY(graph)
        BOOST_SPIRIT_X3_CLASSIFY(lower)
        BOOST_SPIRIT_X3_CLASSIFY(print)
        BOOST_SPIRIT_X3_CLASSIFY(punct)
        BOOST_SPIRIT_X3_CLASSIFY(space)
        BOOST_SPIRIT_X3_CLASSIFY(blank)
        BOOST_SPIRIT_X3_CLASSIFY(upper)

#undef BOOST_SPIRIT_X3_CLASSIFY
    };

    template <typename Encoding, typename Tag>
    struct char_class
      : char_parser<char_class<Encoding, Tag>>
    {
        typedef Encoding encoding;
        typedef Tag tag;
        typedef typename Encoding::char_type char_type;
        typedef char_type attribute_type;
        static bool const has_attribute = true;

        template <typename Char, typename Context>
        bool test(Char ch, Context const&) const
        {
            return ((sizeof(Char) <= sizeof(char_type)) || encoding::ischar(ch))
                && char_class_base<Encoding>::is(tag(), ch);
        }
    };

#define BOOST_SPIRIT_X3_CHAR_CLASS(encoding, name)                                 \
    typedef char_class<char_encoding::encoding, name##_tag> name##_type;        \
    name##_type const name = name##_type();                                     \
    /***/

#define BOOST_SPIRIT_X3_CHAR_CLASSES(encoding)                                     \
    namespace encoding                                                          \
    {                                                                           \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, alnum)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, alpha)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, digit)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, xdigit)                               \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, cntrl)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, graph)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, lower)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, print)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, punct)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, space)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, blank)                                \
        BOOST_SPIRIT_X3_CHAR_CLASS(encoding, upper)                                \
    }                                                                           \
    /***/

    BOOST_SPIRIT_X3_CHAR_CLASSES(standard)
    BOOST_SPIRIT_X3_CHAR_CLASSES(standard_wide)
    BOOST_SPIRIT_X3_CHAR_CLASSES(ascii)
    BOOST_SPIRIT_X3_CHAR_CLASSES(iso8859_1)

#undef BOOST_SPIRIT_X3_CHAR_CLASS
#undef BOOST_SPIRIT_X3_CHAR_CLASSES

    using standard::alnum_type;
    using standard::alpha_type;
    using standard::digit_type;
    using standard::xdigit_type;
    using standard::cntrl_type;
    using standard::graph_type;
    using standard::lower_type;
    using standard::print_type;
    using standard::punct_type;
    using standard::space_type;
    using standard::blank_type;
    using standard::upper_type;

    using standard::alnum;
    using standard::alpha;
    using standard::digit;
    using standard::xdigit;
    using standard::cntrl;
    using standard::graph;
    using standard::lower;
    using standard::print;
    using standard::punct;
    using standard::space;
    using standard::blank;
    using standard::upper;
}}}

#endif

{'repo_name': 'CasparCG/client', 'stars': '152', 'repo_language': 'C++', 'file_name': 'set-variables-and-build-macx.sh', 'mime_type': 'text/x-shellscript', 'hash': -1266558227662958007, 'source_dataset': 'data'}


/*
 * Copyright (C) 2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
 */

#pragma once

#include <wtf/Vector.h>

namespace WebCore {

class RenderedDocumentMarker;

struct MarkedText {
    // Sorted by paint order
    enum Type {
        Unmarked,
        GrammarError,
        Correction,
        SpellingError,
        TextMatch,
        DictationAlternatives,
#if PLATFORM(IOS_FAMILY)
        // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS_FAMILY)-guard.
        DictationPhraseWithAlternatives,
#endif
        Selection,
        DraggedContent,
    };
    unsigned startOffset;
    unsigned endOffset;
    Type type;
    const RenderedDocumentMarker* marker { nullptr };

    bool isEmpty() const { return endOffset <= startOffset; }
    bool operator!=(const MarkedText& other) const { return !(*this == other); }
    bool operator==(const MarkedText& other) const
    {
        return startOffset == other.startOffset && endOffset == other.endOffset && type == other.type && marker == other.marker;
    }
};

enum class OverlapStrategy { None, Frontmost };
WEBCORE_EXPORT Vector<MarkedText> subdivide(const Vector<MarkedText>&, OverlapStrategy = OverlapStrategy::None);

}

{'repo_name': 'ultralight-ux/WebCore', 'stars': '113', 'repo_language': 'C++', 'file_name': 'WebKitTargetConditionals.xcconfig', 'mime_type': 'text/plain', 'hash': 6256608280208242819, 'source_dataset': 'data'}


<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 刘志淳 <chun@engineer.com>
// +----------------------------------------------------------------------

namespace think\console\command\make;

use think\console\command\Make;
use think\console\input\Option;
use think\facade\Config;

class Controller extends Make
{
    protected $type = "Controller";

    protected function configure()
    {
        parent::configure();
        $this->setName('make:controller')
            ->addOption('api', null, Option::VALUE_NONE, 'Generate an api controller class.')
            ->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.')
            ->setDescription('Create a new resource controller class');
    }

    protected function getStub()
    {
        $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;

        if ($this->input->getOption('api')) {
            return $stubPath . 'controller.api.stub';
        }

        if ($this->input->getOption('plain')) {
            return $stubPath . 'controller.plain.stub';
        }

        return $stubPath . 'controller.stub';
    }

    protected function getClassName($name)
    {
        return parent::getClassName($name) . (Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '');
    }

    protected function getNamespace($appNamespace, $module)
    {
        return parent::getNamespace($appNamespace, $module) . '\controller';
    }

}

{'repo_name': 'html580/diygw', 'stars': '224', 'repo_language': 'PHP', 'file_name': 'Systeminfo.php', 'mime_type': 'text/x-php', 'hash': -4186692505486545264, 'source_dataset': 'data'}