text:
<?php

namespace Singo;

use Pimple\ServiceProviderInterface;
use Singo\Contracts\Module\CliCommandProviderInterface;
use Singo\Contracts\Module\CommandHandlerProviderInterface;
use Singo\Contracts\Module\ModuleInterface;
use Singo\Provider\AnnotationRouting;
use Symfony\Component\Console\Application as CLI;

/**
 * Class ModuleBooter
 * @package Singo
 */
trait ModuleBooter
{
    /**
     * boot module
     */
    protected function bootModule()
    {
        $base_namespace = self::$container["config"]->get("modules/base_namespace");
        $modules = self::$container["config"]->get("modules/modules");

        $controllers = [];

        foreach ($modules as $module)
        {
            $module_namespace = $base_namespace . "\\" . key($module);
            $module_class = $module_namespace . "\\Module";
            $module_object = new $module_class();

            if (! $module_object instanceof ModuleInterface) {
                throw new \InvalidArgumentException(
                    "Module {$module_class} must be instance of ServiceProviderInterface"
                );
            }

            /**
             * register service provider and command handler
             */
            $this
                ->bootModuleServiceProvider($module_object)
                ->bootModuleCommand($module_object)
                ->bootModuleCli($module_object);

            /**
             * replace controller with full namespace
             */
            if (isset($module[key($module)]["controllers"])) {
                $routes = $module[key($module)]["controllers"];
                array_walk($routes, function (&$item) use ($module_namespace) {
                    $item = $module_namespace . "\\Controllers\\" . $item;
                });

                $controllers = array_merge($controllers, $routes);
            }
        }

        /**
         * register annotation controller
         */
        self::$container->register(
            new AnnotationRouting(),
            [
                "annot.cache" => (isset(self::$container["cache.factory"])) ? self::$container["cache.factory"] : null,
                "annot.controllers" => $controllers
            ]
        );
    }

    /**
     * @param ModuleInterface $module
     * @return $this
     */
    protected function bootModuleServiceProvider(ModuleInterface $module)
    {
        if ($module instanceof ServiceProviderInterface) {
            self::$container->register($module);
        }

        return $this;
    }

    /**
     * @param ModuleInterface $module
     * @return $this
     */
    protected function bootModuleCommand(ModuleInterface $module)
    {
        if ($module instanceof CommandHandlerProviderInterface) {
            $module->command(self::$container);
        }

        return $this;
    }

    /**
     * @param ModuleInterface $module
     * @return $this
     */
    protected function bootModuleCli(ModuleInterface $module)
    {
        if ($module instanceof CliCommandProviderInterface && $this instanceof CLI) {
            $module->cli($this, self::$container);
        }

        return $this;
    }
}


text:
package com.perimeterx.utils;

import com.perimeterx.models.configuration.ModuleMode;
import com.perimeterx.models.configuration.PXConfiguration;
import org.easymock.EasyMock;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.Map;

@PrepareForTest(FilesUtils.class)
public class FileUtils extends PowerMockTestCase {

    @Test
    public void testReadFileConfigAsMap() throws IOException {
        PowerMock.mockStaticPartial(FilesUtils.class, "readFile");
        EasyMock.expect(FilesUtils.readFile("config.json")).andReturn("{\n" +
                "  \"moduleMode\": 0,\n" +
                "  \"remoteConfigurationInterval\": 1\n" +
                "}\n");
        PowerMock.replayAll();
        Map<String, String> result = FilesUtils.readFileConfigAsMap("config.json");
        Assert.assertEquals(result.get("moduleMode"), "0");
        Assert.assertEquals(result.get("remoteConfigurationInterval"), "1");
    }

    @Test
    public void testReadFile() throws IOException {
        PowerMock.mockStaticPartial(FilesUtils.class, "readFile");
        EasyMock.expect(FilesUtils.readFile("config.json")).andReturn("{\n" +
                "  \"px_module_mode\": 0,\n" +
                "  \"px_remote_configuration_interval_ms\": 1\n" +
                "}\n");
        PowerMock.replayAll();
        PXConfiguration pxConfiguration = new PXConfiguration();
        FilesUtils.readFileConfigAsPXConfig(pxConfiguration, "config.json");
        Assert.assertEquals(pxConfiguration.getModuleMode(), ModuleMode.MONITOR);
        Assert.assertEquals(pxConfiguration.getRemoteConfigurationInterval(), 1);
    }

}

redpajama_set_name: RedPajamaGithub


text:
package com.google.cloud.datastore;

import com.google.cloud.Service;
import com.google.datastore.v1.TransactionOptions;
import java.util.Iterator;
import java.util.List;

/**
 * An interface for Google Cloud Datastore.
 */
public interface Datastore extends Service<DatastoreOptions>, DatastoreReaderWriter {

  /**
   * Returns a new Datastore transaction.
   *
   * @param options a transaction option indicating the mode of the transaction (read-only or read-write)
   * @throws DatastoreException upon failure
   */
  Transaction newTransaction(TransactionOptions options);

  /**
   * Returns a new Datastore transaction.
   *
   * @throws DatastoreException upon failure
   */
  Transaction newTransaction();

  /**
   * A callback for running with a transactional
   * {@link com.google.cloud.datastore.DatastoreReaderWriter}.
   * The associated transaction will be committed after a successful return from the {@code run}
   * method. Any propagated exception will cause the transaction to be rolled-back.
   *
   * @param <T> the type of the return value
   */
  interface TransactionCallable<T> {
    T run(DatastoreReaderWriter readerWriter) throws Exception;
  }

  /**
   * Invokes the callback's {@link Datastore.TransactionCallable#run} method with a
   * {@link DatastoreReaderWriter} that is associated with a new transaction.
   * The transaction will be committed upon successful invocation.
   * Any thrown exception will cause the transaction to rollback and will be propagated
   * as a {@link DatastoreException} with the original exception as its root cause.
   *
   *
Example of running in a transaction.
   *
 {@code
   * String callableResult = "my_callable_result";
   * TransactionCallable<String> callable = new TransactionCallable<String>() {
   *   public String run(DatastoreReaderWriter readerWriter) {
   *     // use readerWriter to run in transaction
   *     return callableResult;
   *   }
   * };
   * String result = datastore.runInTransaction(callable);
   * }

   *
   * @param callable the callback to call with a newly created transactional readerWriter
   * @throws DatastoreException upon failure
   */
  <T> T runInTransaction(TransactionCallable<T> callable);

  /**
   * Invokes the callback's {@link Datastore.TransactionCallable#run} method with a
   * {@link DatastoreReaderWriter} that is associated with a new transaction.
   * The transaction will be committed upon successful invocation.
   * Any thrown exception will cause the transaction to rollback and will be propagated
   * as a {@link DatastoreException} with the original exception as its root cause.
   * If {@link TransactionOptions} is set to read-write mode, previous transaction Id
   * in the options will be automatically populated each time a transaction is retried.
   *
   *
Example of running in a transaction.
   *
 {@code
   * String callableResult = "my_callable_result";
   * TransactionCallable<String> callable = new TransactionCallable<String>() {
   *   public String run(DatastoreReaderWriter readerWriter) {
   *     // use readerWriter to run in transaction
   *     return callableResult;
   *   }
   * };
   *
   * TransactionOptions options = TransactionOptions.newBuilder()
   *     .setReadWrite(TransactionOptions.ReadWrite
   *         .getDefaultInstance())
   *     .build();
   *
   * String result = datastore.runInTransaction(callable, options);
   * }

   *
   * @param callable the callback to call with a newly created transactional readerWriter
   * @param options the Transaction options indicating whether the transaction mode is Read-only or Read-Write
   * @throws DatastoreException upon failure
   */
  <T> T runInTransaction(TransactionCallable<T> callable, TransactionOptions options);

  /**
   * Returns a new Batch for processing multiple write operations in one request.
   *
   *
Example of starting a new batch.
   *
 {@code
   * String keyName1 = "my_key_name_1";
   * String keyName2 = "my_key_name_2";
   * Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
   * Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
   * Batch batch = datastore.newBatch();
   * Entity entity1 = Entity.newBuilder(key1).set("name", "John").build();
   * Entity entity2 = Entity.newBuilder(key2).set("title", "title").build();
   * batch.add(entity1);
   * batch.add(entity2);
   * batch.submit();
   * }

   *
   */
  Batch newBatch();

  /**
   * Allocate a unique id for the given key.
   * The returned key will have the same information (projectId, kind, namespace and ancestors)
   * as the given key and will have a newly assigned id.
   *
   *
Example of allocating an id.
   *
 {@code
   * KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
   * IncompleteKey incompleteKey = keyFactory.newKey();
   *
   * // let cloud datastore automatically assign an id
   * Key key = datastore.allocateId(incompleteKey);
   * }

   *
   * @throws DatastoreException upon failure
   */
  Key allocateId(IncompleteKey key);

  /**
   * Returns a list of keys using the allocated ids ordered by the input.
   *
   *
Example of allocating multiple ids in a single batch.
   *
 {@code
   * KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
   * IncompleteKey incompleteKey1 = keyFactory.newKey();
   * IncompleteKey incompleteKey2 = keyFactory.newKey();
   *
   * // let cloud datastore automatically assign the ids
   * List<Key> keys = datastore.allocateId(incompleteKey1, incompleteKey2);
   * }

   *
   * @throws DatastoreException upon failure
   * @see #allocateId(IncompleteKey)
   */
  List<Key> allocateId(IncompleteKey... keys);

  /**
   * {@inheritDoc}
   *
   *
If an entity for {@code entity.getKey()} does not exist, {@code entity} is inserted.
   * Otherwise, a {@link DatastoreException} is thrown with {@link DatastoreException#getReason()}
   * equal to {@code "ALREADY_EXISTS"}.
   *
   *
Example of adding a single entity.
   *
 {@code
   * String keyName = "my_key_name";
   * Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
   * Entity.Builder entityBuilder = Entity.newBuilder(key);
   * entityBuilder.set("propertyName", "value");
   * Entity entity = entityBuilder.build();
   * try {
   *   datastore.add(entity);
   * } catch (DatastoreException ex) {
   *   if ("ALREADY_EXISTS".equals(ex.getReason())) {
   *     // entity.getKey() already exists
   *   }
   * }
   * }

   *
   * @throws DatastoreException upon failure or if an entity for {@code entity.getKey()} already
   *     exists
   */
  @Override
  Entity add(FullEntity<?> entity);

  /**
   * {@inheritDoc}
   *
   *
If none of entities' keys exist, all entities are inserted. If any of entities' keys already
   * exists the method throws a {@link DatastoreException} with
   * {@link DatastoreException#getReason()} equal to {@code "ALREADY_EXISTS"}. All entities in
   * {@code entities} whose key did not exist are inserted. To achieve a transactional behavior,
   * use {@link Transaction}.
   *
   *
Example of adding multiple entities.
   *
 {@code
   * String keyName1 = "my_key_name1";
   * String keyName2 = "my_key_name2";
   * Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
   * Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
   * entityBuilder1.set("propertyName", "value1");
   * Entity entity1 = entityBuilder1.build();
   *
   * Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
   * Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
   * entityBuilder2.set("propertyName", "value2");
   * Entity entity2 = entityBuilder2.build();
   *
   * try {
   *   datastore.add(entity1, entity2);
   * } catch (DatastoreException ex) {
   *   if ("ALREADY_EXISTS".equals(ex.getReason())) {
   *     // at least one of entity1.getKey() and entity2.getKey() already exists
   *   }
   * }
   * }

   *
   * @throws DatastoreException upon failure or if any of entities' keys already exists
   */
  @Override
  List<Entity> add(FullEntity<?>... entities);

  /**
   * {@inheritDoc}
   *
   *
Example of updating multiple entities.
   *
 {@code
   * String keyName1 = "my_key_name_1";
   * String keyName2 = "my_key_name_2";
   * Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
   * Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
   * entityBuilder1.set("propertyName", "updatedValue1");
   * Entity entity1 = entityBuilder1.build();
   *
   * Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
   * Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
   * entityBuilder2.set("propertyName", "updatedValue2");
   * Entity entity2 = entityBuilder2.build();
   *
   * datastore.update(entity1, entity2);
   * }

   *
   * @throws DatastoreException upon failure
   */
  @Override
  void update(Entity... entities);

  /**
   * {@inheritDoc}
   *
   *
Example of putting a single entity.
   *
 {@code
   * String keyName = "my_key_name";
   * Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
   * Entity.Builder entityBuilder = Entity.newBuilder(key);
   * entityBuilder.set("propertyName", "value");
   * Entity entity = entityBuilder.build();
   * datastore.put(entity);
   * }

   *
   * @throws DatastoreException upon failure
   */
  @Override
  Entity put(FullEntity<?> entity);

  /**
   * {@inheritDoc}
   *
   *
Example of putting multiple entities.
   *
 {@code
   * String keyName1 = "my_key_name1";
   * String keyName2 = "my_key_name2";
   * Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
   * Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
   * entityBuilder1.set("propertyName", "value1");
   * Entity entity1 = entityBuilder1.build();
   *
   * Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
   * Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
   * entityBuilder2.set("propertyName", "value2");
   * Entity entity2 = entityBuilder2.build();
   *
   * datastore.put(entity1, entity2);
   * }

   *
   * @throws DatastoreException upon failure
   */
  @Override
  List<Entity> put(FullEntity<?>... entities);

  /**
   * {@inheritDoc}
   *
   *
Example of deleting multiple entities.
   *
 {@code
   * String keyName1 = "my_key_name1";
   * String keyName2 = "my_key_name2";
   * Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
   * Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
   * datastore.delete(key1, key2);
   * }

   *
   * @throws DatastoreException upon failure
   */
  @Override
  void delete(Key... keys);

  /**
   * Returns a new KeyFactory for this service
   *
   *
Example of creating a {@code KeyFactory}.
   *
 {@code
   * KeyFactory keyFactory = datastore.newKeyFactory();
   * }

   *
   */
  KeyFactory newKeyFactory();

  /**
   * Returns an {@link Entity} for the given {@link Key} or {@code null} if it doesn't exist.
   * {@link ReadOption}s can be specified if desired.
   *
   *
Example of getting an entity.
   *
 {@code
   * String keyName = "my_key_name";
   * Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
   * Entity entity = datastore.get(key);
   * // Do something with the entity
   * }

   *
   * @throws DatastoreException upon failure
   */
  Entity get(Key key, ReadOption... options);

  /**
   * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore. The order of
   * the result is unspecified. Results are loaded lazily, so it is possible to get a
   * {@code DatastoreException} from the returned {@code Iterator}'s
   * {@link Iterator#hasNext hasNext} or {@link Iterator#next next} methods. {@link ReadOption}s can
   * be specified if desired.
   *
   *
Example of getting multiple entity objects.
   *
 {@code
   * String firstKeyName = "my_first_key_name";
   * String secondKeyName = "my_second_key_name";
   * KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
   * Key firstKey = keyFactory.newKey(firstKeyName);
   * Key secondKey = keyFactory.newKey(secondKeyName);
   * Iterator<Entity> entitiesIterator = datastore.get(Lists.newArrayList(firstKey, secondKey));
   * List<Entity> entities = Lists.newArrayList();
   * while (entitiesIterator.hasNext()) {
   *   Entity entity = entitiesIterator.next();
   *   // do something with the entity
   *   entities.add(entity);
   * }
   * }

   *
   * @throws DatastoreException upon failure
   * @see #get(Key)
   */
  Iterator<Entity> get(Iterable<Key> keys, ReadOption... options);

  /**
   * Returns a list with a value for each given key (ordered by input). {@code null} values are
   * returned for nonexistent keys. When possible prefer using {@link #get(Key...)} to avoid eagerly
   * loading the results. {@link ReadOption}s can be specified if desired.
   *
   *
Example of fetching a list of Entity objects.
   *
 {@code
   * String firstKeyName = "my_first_key_name";
   * String secondKeyName = "my_second_key_name";
   * KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
   * Key firstKey = keyFactory.newKey(firstKeyName);
   * Key secondKey = keyFactory.newKey(secondKeyName);
   * List<Entity> entities = datastore.fetch(Lists.newArrayList(firstKey, secondKey));
   * for (Entity entity : entities) {
   *   // do something with the entity
   * }
   * }

   *
   */
  List<Entity> fetch(Iterable<Key> keys, ReadOption... options);

  /**
   * Submits a {@link Query} and returns its result. {@link ReadOption}s can be specified if
   * desired.
   *
   *
Example of running a query to find all entities of one kind.
   *
 {@code
   * String kind = "my_kind";
   * StructuredQuery<Entity> query = Query.newEntityQueryBuilder()
   *     .setKind(kind)
   *     .build();
   * QueryResults<Entity> results = datastore.run(query);
   * List<Entity> entities = Lists.newArrayList();
   * while (results.hasNext()) {
   *   Entity result = results.next();
   *   // do something with result
   *   entities.add(result);
   * }
   * }

   *
   *
Example of running a query to find all entities with a matching property value.
   *
 {@code
   * String kind = "my_kind";
   * String property = "my_property";
   * String value = "my_value";
   * StructuredQuery<Entity> query = Query.newEntityQueryBuilder()
   *     .setKind(kind)
   *     .setFilter(PropertyFilter.eq(property, value))
   *     .build();
   * QueryResults<Entity> results = datastore.run(query);
   * List<Entity> entities = Lists.newArrayList();
   * while (results.hasNext()) {
   *   Entity result = results.next();
   *   // do something with result
   *   entities.add(result);
   * }
   * }

   *
   * @throws DatastoreException upon failure
   */
  <T> QueryResults<T> run(Query<T> query, ReadOption... options);
}


text:
.PHONY: env remove-env test lint web db-migrate db-upgrade

env: remove-env
	python3 -m venv env; \
	source env/bin/activate; \
	pip install -r requirements.txt; \

remove-env:
	rm -rf ./env

test:
	@pytest

lint:
	@flake8 ./web
	@flake8 ./domain

web:
	gunicorn run_web:app

db-up:
	docker-compose up -d

db-down:
	docker-compose down -v

db-migrate:
	python manage.py db migrate

db-upgrade:
	python manage.py db upgrade

db-downgrade:
	python manage.py db downgrade


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Thrift;
using Thrift.Protocol;
using Thrift.Transport;
using Thrift.Server;

namespace ThriftClientTest
{
    class ThirftClient
    {
        static void Main(string[] args) {
            var transport = new TSocket("localhost", 7911);
            var protocol = new TBinaryProtocol(transport);
            var client = new UserStorage.Client(protocol);

            transport.Open();
            var userdata = new UserProfile();
            userdata.Name = "Jeske";

            client.store(userdata);
            transport.Close();
        }
    }
}


text:
import { Conversions } from './conversions';

declare var TextEncoder: any;
declare var TextDecoder: any;
declare var Uint8Array: any;

export class Cryptology {

	constructor() {

	}

	public async exportPublicKey(key) {
		return await window.crypto.subtle.exportKey('spki', key);
	}

	public async exportPrivateKey(key) {
		return await window.crypto.subtle.exportKey('pkcs8', key);
	}

	public async uint8ArrayToHex(data, sep = '') {
		let a, h = '';
		const ch = sep ? sep : '';
		for (let i = 0, len = data.length; i !== len; ++i) {
			a = data[i];
			h += i > 0 ? ch : '';
			h += a < 16 ? '0' : '';
			h += a.toString(16);
		}
		return h;
	}

	public hexToUint8Array(data) {

		if (!data) {
			return new Uint8Array();
		}

		const a = [];
		for (let i = 0, len = data.length; i < len; i += 2) {
			a.push(parseInt(data.substr(i, 2), 16));
		}

		return new Uint8Array(a);

		// var a, h = '';
		// var ch = sep ? sep : '';
		// for (var i = 0, len = data.length; i !== len; ++i) {
		//     a = data[i];
		//     h += i > 0 ? ch : '';
		//     h += a < 16 ? '0' : '';
		//     h += a.toString(16);
		// }
		// return h;
	}

	public stringToArrayBuffer(string) {
		const encoder = new TextEncoder('utf-8');
		return encoder.encode(string);
	}

	// public async performanceTest(mnemonic, passphrase) {

	// 	const startTime2 = new Date();

	// 	for (let i = 0; i < 100; i++) {
	// 		await this.performanceTestInternal(mnemonic, passphrase);
	// 	}

	// 	const endTime2 = new Date();
	// 	const timeDiff2 = endTime2 - startTime2;
	// 	console.log('DIFF2: ' + timeDiff2);
	// }

	// public async performanceTestInternal(mnemonic, passphrase) {

	// 	const buffer = this.stringToArrayBuffer(mnemonic);
	// 	const bufferSalt = this.stringToArrayBuffer('mnemonic' + passphrase);

	// 	// This could be used with async/await pattern, to simplify the code.
	// 	const importedKey = await window.crypto.subtle.importKey(
	// 		'raw',
	// 		buffer,
	// 		{
	// 			name: 'PBKDF2',
	// 		},
	// 		false,
	// 		['deriveKey']
	// 	);

	// 	const derivedKey = await window.crypto.subtle.deriveKey(
	// 		{
	// 			'name': 'PBKDF2',
	// 			salt: bufferSalt,
	// 			iterations: 2048,
	// 			hash: { name: 'SHA-512' },
	// 		},
	// 		importedKey,
	// 		{
	// 			name: 'HMAC',
	// 			hash: 'SHA-512',
	// 			length: 512
	// 		},
	// 		true,
	// 		['sign']
	// 	);

	// 	const exportedKeyHex = window.crypto.subtle.exportKey('raw', derivedKey);

	// 	// .then((exportedKey) => {
	// 	// 	const exportedKeyHex = util.arrayBufferToHexString(exportedKey); // seed == exportedKeyHex - both values should be same.
	// 	// 	const root2 = HDNode.fromSeedHex(exportedKeyHex);
	// 	// });

	// 	// 	.then(function (derivedKey) {
	// 	// 	// derivedKey can be stored to IndexedDB for persistance across browser sessions.
	// 	// 	// This is less secure, but more user friendly, not requiring user to supply their salt (password) on every use.

	// 	// });
	// 	// });
	// }

	public arrayToHexString(byteArray) {
		let hexString = '';
		let nextHexByte;

		for (let i = 0; i < byteArray.byteLength; i++) {
			nextHexByte = byteArray[i].toString(16);  // Integer to base 16
			if (nextHexByte.length < 2) {
				nextHexByte = '0' + nextHexByte;     // Otherwise 10 becomes just a instead of 0a
			}
			hexString += nextHexByte;
		}
		return hexString;
	}

	public arrayBufferToHexString(arrayBuffer) {
		const byteArray = new Uint8Array(arrayBuffer);
		let hexString = '';
		let nextHexByte;

		for (let i = 0; i < byteArray.byteLength; i++) {
			nextHexByte = byteArray[i].toString(16);  // Integer to base 16
			if (nextHexByte.length < 2) {
				nextHexByte = '0' + nextHexByte;     // Otherwise 10 becomes just a instead of 0a
			}
			hexString += nextHexByte;
		}
		return hexString;
	}

	public convertUint8ArrayToText(data) {
		let s = '';
		for (let i = 0, len = data.length; i !== len; ++i) {
			s += String.fromCharCode(data[i]);
		}
		return s;
	}

	public convertUint8ArrayToBase64(data) {
		const s = this.convertUint8ArrayToText(data);
		return window.btoa(s);
	}

	/**
    A string hashing function based on Daniel J. Bernstein's popular 'times 33' hash algorithm.
    @param {string} text - String to hash
    @return {number} Resulting number.
	*/
	public hash(text) {
		'use strict';

		let hash = 5381,
			index = text.length;

		while (index) {
			hash = (hash * 33) ^ text.charCodeAt(--index);
		}

		return hash >>> 0;
	}

	public shortHash(text) {
		return this.hash(text).toString(16);
	}
}


text:
class Solution {
public:
    int getLen(ListNode * head) {
        int len = 0;
        ListNode * cur = head;
        while (cur != NULL) {
            cur = cur->next;
            len++;
        }
        return len;
    }

    ListNode * reverse(ListNode * head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode * prev = head;
        ListNode * cur = head->next;
        while (cur != NULL) {
            ListNode * next = cur->next;
            cur->next = prev;
            prev = cur;
            cur = next;
        }
        head->next = NULL;
        return prev;
    }

    ListNode * getTail(ListNode * head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode * prev = head;
        ListNode * cur = head->next;
        while (cur != NULL) {
            prev = cur;
            cur = cur->next;
        }
        return prev;
    }

    // if n is 0, return the first node
    ListNode * getNthNode(ListNode * head, int n) {
        ListNode * cur = head;
        while (n > 0) {
            cur = cur->next;
            n--;
        }
        return cur;
    }

    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL) return NULL;
        ListNode * newHeadB = reverse(headB);
        ListNode * tailA = getTail(headA);
        if (tailA != headB) { // no intersection
            reverse(newHeadB); // recover the headB
            return NULL;
        }
        int q = getLen(headA);
        int n = getLen(newHeadB);
        reverse(newHeadB); // recover the headB
        int m = getLen(headA);
        int x = (m - n + q) / 2;
        return getNthNode(headA, x);
    }
};


text:
module Onebox
  module Engine
    class GooglePhotosOnebox
      include Engine
      include StandardEmbed

      matches_regexp(/^https?:\/\/(photos)\.(app\.goo\.gl|google\.com)/)
      always_https

      def to_html
        og = get_opengraph
        return video_html(og) if !og.video_secure_url.nil?
        return album_html(og) if !og.type.nil? && og.type == "google_photos:photo_album"
        return image_html(og) if !og.image.nil?
        nil
      end

      private

      def video_html(og)
        escaped_url = ::Onebox::Helpers.normalize_url_for_output(url)

        <<-HTML

              #{raw[:site_name]}

#{og.title}

        HTML
      end

      def album_html(og)
        escaped_url = ::Onebox::Helpers.normalize_url_for_output(url)
        album_title = og.description.nil? ? og.title : "[#{og.description}] #{og.title}"

        <<-HTML

                    #{Onebox::Helpers.truncate(album_title, 80)}

          HTML
      end

      def image_html(og)
        escaped_url = ::Onebox::Helpers.normalize_url_for_output(url)

        <<-HTML

          HTML
      end
    end
  end
end


text:
#include "zincludes.h"
#include "zfilter.h"

/* Forward */
static int zclose_group(NC_GRP_INFO_T*);
static int zclose_gatts(NC_GRP_INFO_T*);
static int zclose_vars(NC_GRP_INFO_T*);
static int zclose_dims(NC_GRP_INFO_T*);
static int zclose_types(NC_GRP_INFO_T*);
static int zclose_type(NC_TYPE_INFO_T* type);
static int zwrite_vars(NC_GRP_INFO_T *grp);

/**************************************************/

/**
 * @internal This function will recurse through an open ZARR file and
 * release resources. All ZARR annotations reclaimed
 *
 * @param file Pointer to ZARR file info struct.
 * @param abort True if this is an abort.
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_ENCZARR could not close the file.
 * @author Dennis Heimbigner
 */
int
ncz_close_file(NC_FILE_INFO_T* file, int abort)
{
    int stat = NC_NOERR;
    NCZ_FILE_INFO_T* zinfo = NULL;

    ZTRACE(2,"file=%s abort=%d",file->hdr.name,abort);

    if(!abort) {
        /* Flush | create all chunks for all vars */
        if((stat=zwrite_vars(file->root_grp))) goto done;
    }

    /* Internal close to reclaim zarr annotations */
    if((stat = zclose_group(file->root_grp)))
	goto done;

    zinfo = file->format_file_info;

    if((stat = nczmap_close(zinfo->map,(abort && zinfo->created)?1:0)))
	goto done;
    NCZ_freestringvec(0,zinfo->envv_controls);
    NC_authfree(zinfo->auth);
    nullfree(zinfo);

done:
    return ZUNTRACE(stat);
}

/**************************************************/
/**
 * @internal Recursively free zarr annotations for a group (and everything
 * it contains).
 *
 * @param grp Pointer to group info struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_group(NC_GRP_INFO_T *grp)
{
    int stat = NC_NOERR;
    NCZ_GRP_INFO_T* zgrp;
    int i;

    assert(grp && grp->format_grp_info != NULL);
    LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));

    /* Recursively call this function for each child, if any, stopping
     * if there is an error. */
    for(i=0; i<ncindexsize(grp->children); i++) {
        if ((stat = zclose_group((NC_GRP_INFO_T*)ncindexith(grp->children,i))))
            goto done;
    }

    /* Close resources associated with global attributes. */
    if ((stat = zclose_gatts(grp)))
        goto done;

    /* Close resources associated with vars. */
    if ((stat = zclose_vars(grp)))
        goto done;

    /* Close resources associated with dims. */
    if ((stat = zclose_dims(grp)))
        goto done;

    /* Close resources associated with types. */
    if ((stat = zclose_types(grp)))
        goto done;

    /* Close the zgroup. */
    zgrp = grp->format_grp_info;
    LOG((4, "%s: closing group %s", __func__, grp->hdr.name));
    nullfree(zgrp);
    grp->format_grp_info = NULL; /* avoid memory errors */

done:
    return stat;
}

/**
 * @internal Close resources for global atts in a group.
 *
 * @param grp Pointer to group info struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_gatts(NC_GRP_INFO_T* grp)
{
    int stat = NC_NOERR;
    NC_ATT_INFO_T *att;
    int a;
    for(a = 0; a < ncindexsize(grp->att); a++) {
        NCZ_ATT_INFO_T* zatt = NULL;
        att = (NC_ATT_INFO_T* )ncindexith(grp->att, a);
        assert(att && att->format_att_info != NULL);
        zatt = att->format_att_info;
	nullfree(zatt);
        att->format_att_info = NULL; /* avoid memory errors */
    }
    return stat;
}

/**
 * @internal Close resources for vars in a group.
 *
 * @param grp Pointer to group info struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_vars(NC_GRP_INFO_T* grp)
{
    int stat = NC_NOERR;
    NC_VAR_INFO_T* var;
    NCZ_VAR_INFO_T* zvar;
    NC_ATT_INFO_T* att;
    int a, i;

    for(i = 0; i < ncindexsize(grp->vars); i++) {
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
        assert(var && var->format_var_info);
        zvar = var->format_var_info;;
        for(a = 0; a < ncindexsize(var->att); a++) {
            NCZ_ATT_INFO_T* zatt;
            att = (NC_ATT_INFO_T*)ncindexith(var->att, a);
            assert(att && att->format_att_info);
            zatt = att->format_att_info;
	    nullfree(zatt);
	    att->format_att_info = NULL; /* avoid memory errors */
        }
#ifdef ENABLE_NCZARR_FILTERS
	/* Reclaim filters */
	if(var->filters != NULL) {
	    (void)NCZ_filter_freelists(var);
	}
	var->filters = NULL;
#endif
	/* Reclaim the type */
	if(var->type_info) (void)zclose_type(var->type_info);
        if(zvar->cache) NCZ_free_chunk_cache(zvar->cache);
	/* reclaim xarray */
	if(zvar->xarray) nclistfreeall(zvar->xarray);
	nullfree(zvar);
	var->format_var_info = NULL; /* avoid memory errors */
    }
    return stat;
}

/**
 * @internal Close resources for dims in a group.
 *
 * @param grp Pointer to group info struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_dims(NC_GRP_INFO_T* grp)
{
    int stat = NC_NOERR;
    NC_DIM_INFO_T* dim;
    int i;

    for(i = 0; i < ncindexsize(grp->dim); i++) {
        NCZ_DIM_INFO_T* zdim;
        dim = (NC_DIM_INFO_T*)ncindexith(grp->dim, i);
        assert(dim && dim->format_dim_info);
        zdim = dim->format_dim_info;
	nullfree(zdim);
	dim->format_dim_info = NULL; /* avoid memory errors */
    }

    return stat;
}

/**
 * @internal Close resources for a single type.  Set values to
 * 0 after closing types. Because of type reference counters, these
 * closes can be called multiple times.
 *
 * @param type Pointer to type struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_type(NC_TYPE_INFO_T* type)
{
    int stat = NC_NOERR;

    assert(type && type->format_type_info != NULL);
    nullfree(type->format_type_info);
    return stat;
}

/**
 * @internal Close resources for types in a group.  Set values to
 * 0 after closing types. Because of type reference counters, these
 * closes can be called multiple times.
 * Warning: note that atomic types are not covered here; this
 * is only for user-defined types.
 *
 * @param grp Pointer to group info struct.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zclose_types(NC_GRP_INFO_T* grp)
{
    int stat = NC_NOERR;
    int i;
    NC_TYPE_INFO_T* type;

    for(i = 0; i < ncindexsize(grp->type); i++)
    {
        type = (NC_TYPE_INFO_T*)ncindexith(grp->type, i);
	if((stat = zclose_type(type))) goto done;
    }
done:
    return stat;
}

/**
 * @internal Recursively flush/create all data for all vars.
 *
 * @param grp Pointer to group info struct whose vars need to be written
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */
static int
zwrite_vars(NC_GRP_INFO_T *grp)
{
    int stat = NC_NOERR;
    int i;

    assert(grp && grp->format_grp_info != NULL);
    LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));

    /* Write all vars for this group breadth first */
    for(i = 0; i < ncindexsize(grp->vars); i++) {
        NC_VAR_INFO_T* var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
	if((stat = ncz_write_var(var))) goto done;
    }

    /* Recursively call this function for each child group, if any, stopping
     * if there is an error. */
    for(i=0; i<ncindexsize(grp->children); i++) {
        if ((stat = zwrite_vars((NC_GRP_INFO_T*)ncindexith(grp->children,i))))
            goto done;
    }

done:
    return stat;
}


text:
from weakref import ref
from decimal import Decimal
import re
import datetime
import traceback, sys

from kivy.app import App
from kivy.cache import Cache
from kivy.clock import Clock
from kivy.compat import string_types
from kivy.properties import (ObjectProperty, DictProperty, NumericProperty,
                             ListProperty, StringProperty)

from kivy.uix.label import Label

from kivy.lang import Builder
from kivy.factory import Factory
from kivy.utils import platform

from electrum.util import profiler, parse_URI, format_time, InvalidPassword, NotEnoughFunds
from electrum import bitcoin
from electrum.util import timestamp_to_datetime
from electrum.plugins import run_hook
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED

from context_menu import ContextMenu

from electrum_gui.kivy.i18n import _

class EmptyLabel(Factory.Label):
    pass

class CScreen(Factory.Screen):
    __events__ = ('on_activate', 'on_deactivate', 'on_enter', 'on_leave')
    action_view = ObjectProperty(None)
    loaded = False
    kvname = None
    context_menu = None
    menu_actions = []
    app = App.get_running_app()

    def _change_action_view(self):
        app = App.get_running_app()
        action_bar = app.root.manager.current_screen.ids.action_bar
        _action_view = self.action_view

        if (not _action_view) or _action_view.parent:
            return
        action_bar.clear_widgets()
        action_bar.add_widget(_action_view)

    def on_enter(self):
        # FIXME: use a proper event don't use animation time of screen
        Clock.schedule_once(lambda dt: self.dispatch('on_activate'), .25)
        pass

    def update(self):
        pass

    @profiler
    def load_screen(self):
        self.screen = Builder.load_file('gui/kivy/uix/ui_screens/' + self.kvname + '.kv')
        self.add_widget(self.screen)
        self.loaded = True
        self.update()
        setattr(self.app, self.kvname + '_screen', self)

    def on_activate(self):
        if self.kvname and not self.loaded:
            self.load_screen()
        #Clock.schedule_once(lambda dt: self._change_action_view())

    def on_leave(self):
        self.dispatch('on_deactivate')

    def on_deactivate(self):
        self.hide_menu()

    def hide_menu(self):
        if self.context_menu is not None:
            self.remove_widget(self.context_menu)
            self.context_menu = None

    def show_menu(self, obj):
        self.hide_menu()
        self.context_menu = ContextMenu(obj, self.menu_actions)
        self.add_widget(self.context_menu)

class HistoryScreen(CScreen):

    tab = ObjectProperty(None)
    kvname = 'history'

    def __init__(self, **kwargs):
        self.ra_dialog = None
        super(HistoryScreen, self).__init__(**kwargs)
        self.menu_actions = [ ('Label', self.label_dialog), ('Details', self.show_tx)]

    def show_tx(self, obj):
        tx_hash = obj.tx_hash
        tx = self.app.wallet.transactions.get(tx_hash)
        if not tx:
            return
        self.app.tx_dialog(tx)

    def label_dialog(self, obj):
        from dialogs.label_dialog import LabelDialog
        key = obj.tx_hash
        text = self.app.wallet.get_label(key)
        def callback(text):
            self.app.wallet.set_label(key, text)
            self.update()
        d = LabelDialog(_('Enter Transaction Label'), text, callback)
        d.open()

    def parse_history(self, items):
        for item in items:
            tx_hash, conf, value, timestamp, balance = item
            time_str = _("unknown")
            if conf > 0:
                try:
                    time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
                except Exception:
                    time_str = _("error")
            if conf == -1:
                time_str = _('Not Verified')
                icon = "atlas://gui/kivy/theming/light/close"
            elif conf == 0:
                time_str = _('Unconfirmed')
                icon = "atlas://gui/kivy/theming/light/unconfirmed"
            elif conf < 6:
                conf = max(1, conf)
                icon = "atlas://gui/kivy/theming/light/clock{}".format(conf)
            else:
                icon = "atlas://gui/kivy/theming/light/confirmed"

            label = self.app.wallet.get_label(tx_hash) if tx_hash else _('Pruned transaction outputs')
            date = timestamp_to_datetime(timestamp)
            quote_text = ''
            if self.app.fiat_unit and date:
                rate = run_hook('history_rate', date)
                if rate:
                    s = run_hook('value_str', value, rate)
                    quote_text = '' if s is None else s + ' ' + self.app.fiat_unit
            yield (conf, icon, time_str, label, value, tx_hash, quote_text)

    def update(self, see_all=False):
        if self.app.wallet is None:
            return

        history = self.parse_history(reversed(
            self.app.wallet.get_history(self.app.current_account)))
        # repopulate History Card
        history_card = self.screen.ids.history_container
        history_card.clear_widgets()
        count = 0
        for item in history:
            count += 1
            conf, icon, date_time, message, value, tx, quote_text = item
            ri = Factory.HistoryItem()
            ri.icon = icon
            ri.date = date_time
            ri.message = message
            ri.value = value
            ri.quote_text = quote_text
            ri.confirmations = conf
            ri.tx_hash = tx
            ri.screen = self
            history_card.add_widget(ri)
            if count == 8 and not see_all:
                break

        if count == 0:
            msg = _('This screen shows your list of transactions. It is currently empty.')
            history_card.add_widget(EmptyLabel(text=msg))

class SendScreen(CScreen):

    kvname = 'send'
    payment_request = None

    def set_URI(self, text):
        import electrum
        try:
            uri = electrum.util.parse_URI(text, self.app.on_pr)
        except:
            self.app.show_info(_("Not a Bitcoin URI"))
            return
        amount = uri.get('amount')
        self.screen.address = uri.get('address', '')
        self.screen.message = uri.get('message', '')
        self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
        self.payment_request = None
        self.screen.is_pr = False

    def update(self):
        pass

    def do_clear(self):
        self.screen.amount = ''
        self.screen.message = ''
        self.screen.address = ''
        self.payment_request = None
        self.screen.is_pr = False

    def set_request(self, pr):
        self.screen.address = pr.get_requestor()
        amount = pr.get_amount()
        self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
        self.screen.message = pr.get_memo()
        if pr.is_pr():
            self.screen.is_pr = True
            self.payment_request = pr
        else:
            self.screen.is_pr = False
            self.payment_request = None

    def do_save(self):
        if not self.screen.address:
            return
        if self.screen.is_pr:
            # it sould be already saved
            return
        # save address as invoice
        from electrum.paymentrequest import make_unsigned_request, PaymentRequest
        req = {'address':self.screen.address, 'memo':self.screen.message}
        amount = self.app.get_amount(self.screen.amount) if self.screen.amount else 0
        req['amount'] = amount
        pr = make_unsigned_request(req).SerializeToString()
        pr = PaymentRequest(pr)
        self.app.invoices.add(pr)
        self.app.update_tab('invoices')
        self.app.show_info(_("Invoice saved"))
        if pr.is_pr():
            self.screen.is_pr = True
            self.payment_request = pr
        else:
            self.screen.is_pr = False
            self.payment_request = None

    def do_paste(self):
        contents = unicode(self.app._clipboard.paste())
        if not contents:
            self.app.show_info(_("Clipboard is empty"))
            return
        self.set_URI(contents)

    def do_send(self):
        if self.screen.is_pr:
            if self.payment_request.has_expired():
                self.app.show_error(_('Payment request has expired'))
                return
            outputs = self.payment_request.get_outputs()
        else:
            address = str(self.screen.address)
            if not address:
                self.app.show_error(_('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request'))
                return
            if not bitcoin.is_address(address):
                self.app.show_error(_('Invalid Bitcoin Address') + ':\n' + address)
                return
            try:
                amount = self.app.get_amount(self.screen.amount)
            except:
                self.app.show_error(_('Invalid amount') + ':\n' + self.screen.amount)
                return
            outputs = [(bitcoin.TYPE_ADDRESS, address, amount)]
        message = unicode(self.screen.message)
        amount = sum(map(lambda x:x[2], outputs))
        # make unsigned transaction
        coins = self.app.wallet.get_spendable_coins()
        config = self.app.electrum_config
        try:
            tx = self.app.wallet.make_unsigned_transaction(coins, outputs, config, None)
        except NotEnoughFunds:
            self.app.show_error(_("Not enough funds"))
            return
        except Exception as e:
            traceback.print_exc(file=sys.stdout)
            self.app.show_error(str(e))
            return
        fee = tx.get_fee()
        msg = [
            _("Amount to be sent") + ": " + self.app.format_amount_and_units(amount),
            _("Mining fee") + ": " + self.app.format_amount_and_units(fee),
        ]
        if fee >= config.get('confirm_fee', 100000):
            msg.append(_('Warning')+ ': ' + _("The fee for this transaction seems unusually high."))
        msg.append(_("Enter your PIN code to proceed"))
        self.app.protected('\n'.join(msg), self.send_tx, (tx, message))

    def send_tx(self, tx, message, password):
        def on_success(tx):
            if tx.is_complete():
                self.app.broadcast(tx, self.payment_request)
                self.app.wallet.set_label(tx.hash(), message)
            else:
                self.app.tx_dialog(tx)
        def on_failure(error):
            self.app.show_error(error)
        if self.app.wallet.can_sign(tx):
            self.app.show_info("Signing...")
            self.app.sign_tx(tx, password, on_success, on_failure)
        else:
            self.app.tx_dialog(tx)

class ReceiveScreen(CScreen):

    kvname = 'receive'

    def update(self):
        if not self.screen.address:
            self.get_new_address()
        else:
            status = self.app.wallet.get_request_status(self.screen.address)
            self.screen.status = _('Payment received') if status == PR_PAID else ''

    def clear(self):
        self.screen.address = ''
        self.screen.amount = ''
        self.screen.message = ''

    def get_new_address(self):
        if not self.app.wallet:
            return False
        addr = self.app.wallet.get_unused_address(None)
        if addr is None:
            return False
        self.clear()
        self.screen.address = addr
        return True

    def on_address(self, addr):
        req = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
        self.screen.status = ''
        if req:
            self.screen.message = unicode(req.get('memo', ''))
            amount = req.get('amount')
            self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
            status = req.get('status', PR_UNKNOWN)
            self.screen.status = _('Payment received') if status == PR_PAID else ''
        Clock.schedule_once(lambda dt: self.update_qr())

    def get_URI(self):
        from electrum.util import create_URI
        amount = self.screen.amount
        if amount:
            a, u = self.screen.amount.split()
            assert u == self.app.base_unit
            amount = Decimal(a) * pow(10, self.app.decimal_point())
        return create_URI(self.screen.address, amount, self.screen.message)

    @profiler
    def update_qr(self):
        uri = self.get_URI()
        qr = self.screen.ids.qr
        qr.set_data(uri)

    def do_share(self):
        if platform != 'android':
            return
        uri = self.get_URI()
        from jnius import autoclass, cast
        JS = autoclass('java.lang.String')
        Intent = autoclass('android.content.Intent')
        sendIntent = Intent()
        sendIntent.setAction(Intent.ACTION_SEND)
        sendIntent.setType("text/plain")
        sendIntent.putExtra(Intent.EXTRA_TEXT, JS(uri))
        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        currentActivity = cast('android.app.Activity', PythonActivity.mActivity)
        it = Intent.createChooser(sendIntent, cast('java.lang.CharSequence', JS("Share Bitcoin Request")))
        currentActivity.startActivity(it)

    def do_copy(self):
        uri = self.get_URI()
        self.app._clipboard.copy(uri)
        self.app.show_info(_('Request copied to clipboard'))

    def save_request(self):
        addr = str(self.screen.address)
        amount = str(self.screen.amount)
        message = str(self.screen.message)
        amount = self.app.get_amount(amount) if amount else 0
        req = self.app.wallet.make_payment_request(addr, amount, message, None)
        self.app.wallet.add_payment_request(req, self.app.electrum_config)
        self.app.update_tab('requests')

    def on_amount_or_message(self):
        self.save_request()
        Clock.schedule_once(lambda dt: self.update_qr())

    def do_new(self):
        addr = self.get_new_address()
        if not addr:
            self.app.show_info(_('Please use the existing requests first.'))
        else:
            self.save_request()
            self.app.show_info(_('New request added to your list.'))

invoice_text = {
    PR_UNPAID:_('Pending'),
    PR_UNKNOWN:_('Unknown'),
    PR_PAID:_('Paid'),
    PR_EXPIRED:_('Expired')
}
request_text = {
    PR_UNPAID: _('Pending'),
    PR_UNKNOWN: _('Unknown'),
    PR_PAID: _('Received'),
    PR_EXPIRED: _('Expired')
}
pr_icon = {
    PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
    PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
    PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
    PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
}

class InvoicesScreen(CScreen):
    kvname = 'invoices'

    def update(self):
        self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
        invoices_list = self.screen.ids.invoices_container
        invoices_list.clear_widgets()

        _list = self.app.invoices.sorted_list()
        for pr in _list:
            ci = Factory.InvoiceItem()
            ci.key = pr.get_id()
            ci.requestor = pr.get_requestor()
            ci.memo = pr.get_memo()
            amount = pr.get_amount()
            if amount:
                ci.amount = self.app.format_amount_and_units(amount)
                status = self.app.invoices.get_status(ci.key)
                ci.status = invoice_text[status]
                ci.icon = pr_icon[status]
            else:
                ci.amount = _('No Amount')
                ci.status = ''
            exp = pr.get_expiration_date()
            ci.date = format_time(exp) if exp else _('Never')
            ci.screen = self
            invoices_list.add_widget(ci)

        if not _list:
            msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.')
            invoices_list.add_widget(EmptyLabel(text=msg))

    def do_pay(self, obj):
        pr = self.app.invoices.get(obj.key)
        self.app.on_pr(pr)

    def do_view(self, obj):
        pr = self.app.invoices.get(obj.key)
        pr.verify(self.app.contacts)
        self.app.show_pr_details(pr.get_dict(), obj.status, True)

    def do_delete(self, obj):
        from dialogs.question import Question
        def cb():
            self.app.invoices.remove(obj.key)
            self.app.update_tab('invoices')
        d = Question(_('Delete invoice?'), cb)
        d.open()

class RequestsScreen(CScreen):
    kvname = 'requests'

    def update(self):
        self.menu_actions = [('Show', self.do_show), ('Details', self.do_view), ('Delete', self.do_delete)]
        requests_list = self.screen.ids.requests_container
        requests_list.clear_widgets()
        _list = self.app.wallet.get_sorted_requests(self.app.electrum_config) if self.app.wallet else []
        for req in _list:
            address = req['address']
            timestamp = req.get('time', 0)
            amount = req.get('amount')
            expiration = req.get('exp', None)
            status = req.get('status')
            signature = req.get('sig')

            ci = Factory.RequestItem()
            ci.address = address
            ci.memo = self.app.wallet.get_label(address)
            if amount:
                status = req.get('status')
                ci.status = request_text[status]
            else:
                received = self.app.wallet.get_addr_received(address)
                ci.status = self.app.format_amount_and_units(amount)

            ci.icon = pr_icon[status]
            ci.amount = self.app.format_amount_and_units(amount) if amount else _('No Amount')
            ci.date = format_time(timestamp)
            ci.screen = self
            requests_list.add_widget(ci)

        if not _list:
            msg = _('This screen shows the list of payment requests you made.')
            requests_list.add_widget(EmptyLabel(text=msg))

    def do_show(self, obj):
        self.app.show_request(obj.address)

    def do_view(self, obj):
        req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config)
        status = req.get('status')
        amount = req.get('amount')
        address = req['address']
        if amount:
            status = req.get('status')
            status = request_text[status]
        else:
            received_amount = self.app.wallet.get_addr_received(address)
            status = self.app.format_amount_and_units(received_amount)

        self.app.show_pr_details(req, status, False)

    def do_delete(self, obj):
        from dialogs.question import Question
        def cb():
            self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config)
            self.update()
        d = Question(_('Delete request?'), cb)
        d.open()

class TabbedCarousel(Factory.TabbedPanel):
    '''Custom TabbedPanel using a carousel used in the Main Screen
    '''

    carousel = ObjectProperty(None)

    def animate_tab_to_center(self, value):
        scrlv = self._tab_strip.parent
        if not scrlv:
            return
        idx = self.tab_list.index(value)
        n = len(self.tab_list)
        if idx in [0, 1]:
            scroll_x = 1
        elif idx in [n-1, n-2]:
            scroll_x = 0
        else:
            scroll_x = 1. * (n - idx - 1) / (n - 1)
        mation = Factory.Animation(scroll_x=scroll_x, d=.25)
        mation.cancel_all(scrlv)
        mation.start(scrlv)

    def on_current_tab(self, instance, value):
        self.animate_tab_to_center(value)

    def on_index(self, instance, value):
        current_slide = instance.current_slide
        if not hasattr(current_slide, 'tab'):
            return
        tab = current_slide.tab
        ct = self.current_tab
        try:
            if ct.text != tab.text:
                carousel = self.carousel
                carousel.slides[ct.slide].dispatch('on_leave')
                self.switch_to(tab)
                carousel.slides[tab.slide].dispatch('on_enter')
        except AttributeError:
            current_slide.dispatch('on_enter')

    def switch_to(self, header):
        # we have to replace the functionality of the original switch_to
        if not header:
            return
        if not hasattr(header, 'slide'):
            header.content = self.carousel
            super(TabbedCarousel, self).switch_to(header)
            try:
                tab = self.tab_list[-1]
            except IndexError:
                return
            self._current_tab = tab
            tab.state = 'down'
            return

        carousel = self.carousel
        self.current_tab.state = "normal"
        header.state = 'down'
        self._current_tab = header
        # set the carousel to load  the appropriate slide
        # saved in the screen attribute of the tab head
        slide = carousel.slides[header.slide]
        if carousel.current_slide != slide:
            carousel.current_slide.dispatch('on_leave')
            carousel.load_slide(slide)
            slide.dispatch('on_enter')

    def add_widget(self, widget, index=0):
        if isinstance(widget, Factory.CScreen):
            self.carousel.add_widget(widget)
            return
        super(TabbedCarousel, self).add_widget(widget, index=index)


text:
package com.google.appengine.datanucleus.jpa;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceConfig;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.TransactionOptions;
import com.google.appengine.datanucleus.DatastoreServiceFactoryInternal;
import com.google.appengine.datanucleus.DatastoreServiceInterceptor;
import com.google.appengine.datanucleus.Utils;
import com.google.appengine.datanucleus.test.jpa.BidirectionalChildJPA;
import com.google.appengine.datanucleus.test.jpa.BidirectionalChildLongPkJPA;
import com.google.appengine.datanucleus.test.jpa.BidirectionalChildUnencodedStringPkJPA;
import com.google.appengine.datanucleus.test.jpa.Book;
import com.google.appengine.datanucleus.test.jpa.HasKeyPkJPA;
import com.google.appengine.datanucleus.test.jpa.HasOneToManyJPA;
import com.google.appengine.datanucleus.test.jpa.HasOneToManyKeyPkJPA;
import com.google.appengine.datanucleus.test.jpa.HasOneToManyLongPkJPA;
import com.google.appengine.datanucleus.test.jpa.HasOneToManyUnencodedStringPkJPA;
import com.google.appengine.datanucleus.test.jpa.HasOneToManyWithOrderByJPA;

import org.easymock.EasyMock;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

import javax.persistence.PersistenceException;

import static com.google.appengine.datanucleus.TestUtils.assertKeyParentEquals;

/**
 * @author Max Ross <maxr@google.com>
 */
abstract class JPAOneToManyTestCase extends JPATestCase {

  void testInsert_NewParentAndChild(BidirectionalChildJPA bidirChild, HasOneToManyJPA parent,
                                    StartEnd startEnd)
      throws Exception {
    bidirChild.setChildVal("yam");

    Book b = newBook();

    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
    hasKeyPk.setStr("yag");

    parent.getBidirChildren().add(bidirChild);
    bidirChild.setParent(parent);
    parent.getBooks().add(b);
    parent.getHasKeyPks().add(hasKeyPk);
    parent.setVal("yar");

    startEnd.start();
    em.persist(parent);
    startEnd.end();

    assertNotNull(bidirChild.getId());
    assertNotNull(b.getId());
    assertNotNull(hasKeyPk.getId());

    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
    assertNotNull(bidirChildEntity);
    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
    assertKeyParentEquals(parent.getId(), bidirChildEntity, bidirChild.getId());

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    assertNotNull(bookEntity);
    assertEquals("max", bookEntity.getProperty("author"));
    assertEquals("22333", bookEntity.getProperty("isbn"));
    assertEquals("yam", bookEntity.getProperty("title"));
    assertEquals(KeyFactory.stringToKey(b.getId()), bookEntity.getKey());
    assertKeyParentEquals(parent.getId(), bookEntity, b.getId());

    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
    assertNotNull(hasKeyPkEntity);
    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
    assertEquals(hasKeyPk.getId(), hasKeyPkEntity.getKey());
    assertKeyParentEquals(parent.getId(), hasKeyPkEntity, hasKeyPk.getId());

    Entity parentEntity = ds.get(KeyFactory.stringToKey(parent.getId()));
    assertNotNull(parentEntity);
    assertEquals(4, parentEntity.getProperties().size());
    assertEquals("yar", parentEntity.getProperty("val"));
    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
    assertEquals(Collections.singletonList(bookEntity.getKey()), parentEntity.getProperty("books"));
    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));

    assertCountsInDatastore(parent.getClass(), bidirChild.getClass(), 1, 1);
  }

  void testInsert_ExistingParentNewChild(BidirectionalChildJPA bidirChild, HasOneToManyJPA pojo,
                                         StartEnd startEnd) throws Exception {
    pojo.setVal("yar");

    startEnd.start();
    em.persist(pojo);
    startEnd.end();
    assertNotNull(pojo.getId());
    assertTrue(pojo.getBooks().isEmpty());
    assertTrue(pojo.getHasKeyPks().isEmpty());
    assertTrue(pojo.getBidirChildren().isEmpty());

    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertNotNull(pojoEntity);
    assertEquals(4, pojoEntity.getProperties().size());
    assertTrue(pojoEntity.hasProperty("bidirChildren"));
    assertNull(pojoEntity.getProperty("bidirChildren"));
    assertTrue(pojoEntity.hasProperty("books"));
    assertNull(pojoEntity.getProperty("books"));
    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
    assertNull(pojoEntity.getProperty("hasKeyPks"));

    startEnd.start();
    Book b = newBook();
    pojo.getBooks().add(b);

    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
    hasKeyPk.setStr("yag");
    pojo.getHasKeyPks().add(hasKeyPk);

    bidirChild.setChildVal("yam");
    pojo.getBidirChildren().add(bidirChild);
    bidirChild.setParent(pojo);

    em.merge(pojo);
    startEnd.end();

    assertNotNull(bidirChild.getId());
    assertNotNull(bidirChild.getParent());
    assertNotNull(b.getId());
    assertNotNull(hasKeyPk.getId());

    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
    assertNotNull(bidirChildEntity);
    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidirChild.getId());

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    assertNotNull(bookEntity);
    assertEquals("max", bookEntity.getProperty("author"));
    assertEquals("22333", bookEntity.getProperty("isbn"));
    assertEquals("yam", bookEntity.getProperty("title"));
    assertEquals(KeyFactory.stringToKey(b.getId()), bookEntity.getKey());
    assertKeyParentEquals(pojo.getId(), bookEntity, b.getId());

    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
    assertNotNull(hasKeyPkEntity);
    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
    assertEquals(hasKeyPk.getId(), hasKeyPkEntity.getKey());
    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());

    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertNotNull(parentEntity);
    assertEquals(4, parentEntity.getProperties().size());
    assertEquals("yar", parentEntity.getProperty("val"));
    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
    assertEquals(Collections.singletonList(bookEntity.getKey()), parentEntity.getProperty("books"));
    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));

    assertCountsInDatastore(pojo.getClass(), bidirChild.getClass(), 1, 1);
  }

  void testUpdate_UpdateChildWithMerge(BidirectionalChildJPA bidir, HasOneToManyJPA pojo,
      StartEnd startEnd) throws Exception {
    Book b = newBook();
    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();

    pojo.getBooks().add(b);
    pojo.getHasKeyPks().add(hasKeyPk);
    pojo.getBidirChildren().add(bidir);
    bidir.setParent(pojo);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    assertNotNull(b.getId());
    assertNotNull(hasKeyPk.getId());
    assertNotNull(bidir.getId());
    assertNotNull(pojo.getId());

    startEnd.start();
    b.setIsbn("yam");
    hasKeyPk.setStr("yar");
    bidir.setChildVal("yap");
    em.merge(pojo);
    startEnd.end();

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    assertNotNull(bookEntity);
    assertEquals("yam", bookEntity.getProperty("isbn"));
    assertKeyParentEquals(pojo.getId(), bookEntity, b.getId());

    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
    assertNotNull(hasKeyPkEntity);
    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());

    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
    assertNotNull(bidirEntity);
    assertEquals("yap", bidirEntity.getProperty("childVal"));
    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
  }

  void testUpdate_UpdateChild(BidirectionalChildJPA bidir, HasOneToManyJPA pojo,
                              StartEnd startEnd) throws Exception {
    Book b = newBook();
    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();

    pojo.getBooks().add(b);
    pojo.getHasKeyPks().add(hasKeyPk);
    pojo.getBidirChildren().add(bidir);
    bidir.setParent(pojo);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    assertNotNull(b.getId());
    assertNotNull(hasKeyPk.getId());
    assertNotNull(bidir.getId());
    assertNotNull(pojo.getId());

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    pojo.getBooks().iterator().next().setIsbn("yam");
    pojo.getHasKeyPks().iterator().next().setStr("yar");
    pojo.getBidirChildren().iterator().next().setChildVal("yap");
    startEnd.end();

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    assertNotNull(bookEntity);
    assertEquals("yam", bookEntity.getProperty("isbn"));
    assertKeyParentEquals(pojo.getId(), bookEntity, b.getId());

    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
    assertNotNull(hasKeyPkEntity);
    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());

    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
    assertNotNull(bidirEntity);
    assertEquals("yap", bidirEntity.getProperty("childVal"));
    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
  }

  void testUpdate_NullOutChildren(BidirectionalChildJPA bidir, HasOneToManyJPA pojo,
                                  StartEnd startEnd) throws Exception {
    Book b = newBook();
    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();

    pojo.getBooks().add(b);
    pojo.getHasKeyPks().add(hasKeyPk);
    pojo.getBidirChildren().add(bidir);
    bidir.setParent(pojo);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);

    startEnd.start();
    pojo.nullBooks();
    pojo.nullHasKeyPks();
    pojo.nullBidirChildren();
    em.merge(pojo);
    startEnd.end();

    try {
      ds.get(KeyFactory.stringToKey(b.getId()));
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    try {
      ds.get(hasKeyPk.getId());
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    try {
      ds.get(KeyFactory.stringToKey(bidir.getId()));
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertEquals(4, pojoEntity.getProperties().size());
    assertTrue(pojoEntity.hasProperty("bidirChildren"));
    assertNull(pojoEntity.getProperty("bidirChildren"));
    assertTrue(pojoEntity.hasProperty("books"));
    assertNull(pojoEntity.getProperty("books"));
    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
    assertNull(pojoEntity.getProperty("hasKeyPks"));

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 0);
  }

  void testUpdate_ClearOutChildren(BidirectionalChildJPA bidir, HasOneToManyJPA pojo,
                                   StartEnd startEnd) throws Exception {
    Book b = newBook();
    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();

    pojo.getBooks().add(b);
    pojo.getHasKeyPks().add(hasKeyPk);
    pojo.getBidirChildren().add(bidir);
    bidir.setParent(pojo);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);

    startEnd.start();
    pojo.getBooks().clear();
    pojo.getHasKeyPks().clear();
    pojo.getBidirChildren().clear();
    em.merge(pojo);
    startEnd.end();

    try {
      ds.get(KeyFactory.stringToKey(b.getId()));
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    try {
      ds.get(hasKeyPk.getId());
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    try {
      ds.get(KeyFactory.stringToKey(bidir.getId()));
      fail("expected enfe");
    } catch (EntityNotFoundException enfe) {
      // good
    }

    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertEquals(4, pojoEntity.getProperties().size());
    assertTrue(pojoEntity.hasProperty("bidirChildren"));
    assertNull(pojoEntity.getProperty("bidirChildren"));
    assertTrue(pojoEntity.hasProperty("books"));
    assertNull(pojoEntity.getProperty("books"));
    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
    assertNull(pojoEntity.getProperty("hasKeyPks"));

    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 0);
  }

  void testFindWithOrderBy(Class<? extends HasOneToManyWithOrderByJPA> pojoClass,
                           StartEnd startEnd) throws Exception {
    getExecutionContext().getNucleusContext().getPersistenceConfiguration().setProperty(
        "datanucleus.appengine.allowMultipleRelationsOfSameType", true);

    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
    ds.put(pojoEntity);

    Entity bookEntity1 = Book.newBookEntity(pojoEntity.getKey(), "auth1", "22222", "title 1");
    ds.put(bookEntity1);

    Entity bookEntity2 = Book.newBookEntity(pojoEntity.getKey(), "auth2", "22222", "title 2");
    ds.put(bookEntity2);

    Entity bookEntity3 = Book.newBookEntity(pojoEntity.getKey(), "auth1", "22221", "title 0");
    ds.put(bookEntity3);

    pojoEntity.setProperty("booksByAuthorAndTitle", Utils.newArrayList(bookEntity2.getKey(), bookEntity3.getKey(), bookEntity1.getKey()));
    pojoEntity.setProperty("booksByIdAndAuthor", Utils.newArrayList(bookEntity3.getKey(), bookEntity2.getKey(), bookEntity1.getKey()));
    pojoEntity.setProperty("booksByAuthorAndId", Utils.newArrayList(bookEntity2.getKey(), bookEntity1.getKey(), bookEntity3.getKey()));
    ds.put(pojoEntity);

    startEnd.start();
    HasOneToManyWithOrderByJPA pojo =
        em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
    assertNotNull(pojo);
    assertNotNull(pojo.getBooksByAuthorAndTitle());
    assertEquals(3, pojo.getBooksByAuthorAndTitle().size());
    assertEquals("title 2", pojo.getBooksByAuthorAndTitle().get(0).getTitle());
    assertEquals("title 0", pojo.getBooksByAuthorAndTitle().get(1).getTitle());
    assertEquals("title 1", pojo.getBooksByAuthorAndTitle().get(2).getTitle());

    assertNotNull(pojo.getBooksByIdAndAuthor());
    assertEquals(3, pojo.getBooksByIdAndAuthor().size());
    assertEquals("title 0", pojo.getBooksByIdAndAuthor().get(0).getTitle());
    assertEquals("title 2", pojo.getBooksByIdAndAuthor().get(1).getTitle());
    assertEquals("title 1", pojo.getBooksByIdAndAuthor().get(2).getTitle());

    assertNotNull(pojo.getBooksByAuthorAndId());
    assertEquals(3, pojo.getBooksByAuthorAndId().size());
    assertEquals("title 2", pojo.getBooksByAuthorAndId().get(0).getTitle());
    assertEquals("title 1", pojo.getBooksByAuthorAndId().get(1).getTitle());
    assertEquals("title 0", pojo.getBooksByAuthorAndId().get(2).getTitle());

    startEnd.end();
  }

  void testFind(Class<? extends HasOneToManyJPA> pojoClass,
                Class<? extends BidirectionalChildJPA> bidirClass,
                StartEnd startEnd) throws Exception {
    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
    ds.put(pojoEntity);

    Entity bookEntity = Book.newBookEntity(pojoEntity.getKey(), "auth1", "22222", "title 1");
    ds.put(bookEntity);

    Entity hasKeyPkEntity = new Entity(HasKeyPkJPA.class.getSimpleName(), pojoEntity.getKey());
    hasKeyPkEntity.setProperty("str", "yar");
    ds.put(hasKeyPkEntity);

    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
    bidirEntity.setProperty("childVal", "yap");
    ds.put(bidirEntity);

    pojoEntity.setProperty("books", Utils.newArrayList(bookEntity.getKey()));
    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
    ds.put(pojoEntity);

    startEnd.start();
    HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
    assertNotNull(pojo);
    assertNotNull(pojo.getBooks());
    assertEquals(1, pojo.getBooks().size());
    assertEquals("auth1", pojo.getBooks().iterator().next().getAuthor());
    assertNotNull(pojo.getHasKeyPks());
    assertEquals(1, pojo.getHasKeyPks().size());
    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
    assertNotNull(pojo.getBidirChildren());
    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
    startEnd.end();
  }

  void testQuery(Class<? extends HasOneToManyJPA> pojoClass,
                 Class<? extends BidirectionalChildJPA> bidirClass,
                 StartEnd startEnd) throws Exception {
    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
    ds.put(pojoEntity);

    Entity bookEntity = Book.newBookEntity(pojoEntity.getKey(), "auth", "22222", "the title");
    ds.put(bookEntity);

    Entity hasKeyPkEntity = new Entity(HasKeyPkJPA.class.getSimpleName(), pojoEntity.getKey());
    hasKeyPkEntity.setProperty("str", "yar");
    ds.put(hasKeyPkEntity);

    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
    bidirEntity.setProperty("childVal", "yap");
    ds.put(bidirEntity);

    pojoEntity.setProperty("books", Utils.newArrayList(bookEntity.getKey()));
    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
    ds.put(pojoEntity);

    javax.persistence.Query q = em.createQuery(
        "select from " + pojoClass.getName() + " b where id = :key");
    q.setParameter("key", KeyFactory.keyToString(pojoEntity.getKey()));
    startEnd.start();
    @SuppressWarnings("unchecked")
    List<HasOneToManyJPA> result = (List<HasOneToManyJPA>) q.getResultList();
    assertEquals(1, result.size());
    HasOneToManyJPA pojo = result.get(0);
    assertNotNull(pojo.getBooks());
    assertEquals(1, pojo.getBooks().size());
    assertEquals("auth", pojo.getBooks().iterator().next().getAuthor());
    assertEquals(1, pojo.getBooks().size());
    assertNotNull(pojo.getHasKeyPks());
    assertEquals(1, pojo.getHasKeyPks().size());
    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
    assertNotNull(pojo.getBidirChildren());
    assertEquals(1, pojo.getBidirChildren().size());
    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
    startEnd.end();
  }

  void testChildFetchedLazily(Class<? extends HasOneToManyJPA> pojoClass,
                              Class<? extends BidirectionalChildJPA> bidirClass) throws Exception {
    DatastoreServiceConfig config = getStoreManager().getDefaultDatastoreServiceConfigForReads();
    // force a new emf to get created after we've installed our own
    // DatastoreService mock
    emf.close();
    tearDown();
    DatastoreService mockDatastore = EasyMock.createMock(DatastoreService.class);
    DatastoreService original = DatastoreServiceFactoryInternal.getDatastoreService(config);
    DatastoreServiceFactoryInternal.setDatastoreService(mockDatastore);
    try {
      setUp();

      Entity pojoEntity = new Entity(pojoClass.getSimpleName());
      ds.put(pojoEntity);

      Entity bookEntity = Book.newBookEntity(pojoEntity.getKey(), "auth", "22222", "the title");
      ds.put(bookEntity);

      Entity hasKeyPkEntity = new Entity(HasKeyPkJPA.class.getSimpleName(), pojoEntity.getKey());
      hasKeyPkEntity.setProperty("str", "yar");
      ds.put(hasKeyPkEntity);

      Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
      bidirEntity.setProperty("childVal", "yap");
      ds.put(bidirEntity);

      Transaction txn = EasyMock.createMock(Transaction.class);
      EasyMock.expect(txn.getId()).andReturn("1").times(2);
      txn.commit();
      EasyMock.expectLastCall();
      EasyMock.replay(txn);
      EasyMock.expect(mockDatastore.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(txn);
      // the only get we're going to perform is for the pojo
      EasyMock.expect(mockDatastore.get(txn, pojoEntity.getKey())).andReturn(pojoEntity);
      EasyMock.replay(mockDatastore);

      beginTxn();
      HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
      assertNotNull(pojo);
      pojo.getId();
      commitTxn();
    } finally {
      // need to close the pmf before we restore the original datastore service
      emf.close();
      DatastoreServiceFactoryInternal.setDatastoreService(original);
    }
    EasyMock.verify(mockDatastore);
  }

  void testDeleteParentDeletesChild(Class<? extends HasOneToManyJPA> pojoClass,
                                    Class<? extends BidirectionalChildJPA> bidirClass,
                                    StartEnd startEnd) throws Exception {
    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
    ds.put(pojoEntity);

    Entity bookEntity = Book.newBookEntity(pojoEntity.getKey(), "auth", "22222", "the title");
    ds.put(bookEntity);

    Entity hasKeyPkEntity = new Entity(HasKeyPkJPA.class.getSimpleName(), pojoEntity.getKey());
    hasKeyPkEntity.setProperty("str", "yar");
    ds.put(hasKeyPkEntity);

    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
    bidirEntity.setProperty("childVal", "yap");
    ds.put(bidirEntity);

    pojoEntity.setProperty("books", Utils.newArrayList(bookEntity.getKey()));
    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
    ds.put(pojoEntity);

    startEnd.start();
    HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
    em.remove(pojo);
    startEnd.end();
    assertCountsInDatastore(pojoClass, bidirClass, 0, 0);
  }

  private static int nextId = 0;

  static String nextNamedKey() {
    return "a" + nextId++;
  }

  public void testRemoveObject(HasOneToManyJPA pojo, BidirectionalChildJPA bidir1,
                               BidirectionalChildJPA bidir2,
                               StartEnd startEnd) throws Exception {
    pojo.setVal("yar");
    bidir1.setChildVal("yam1");
    bidir2.setChildVal("yam2");
    Book b1 = newBook();
    Book b2 = newBook();
    b2.setTitle("another title");
    HasKeyPkJPA hasKeyPk1 = new HasKeyPkJPA(nextNamedKey());
    HasKeyPkJPA hasKeyPk2 = new HasKeyPkJPA(nextNamedKey());
    hasKeyPk2.setStr("yar 2");
    pojo.getBooks().add(b1);
    pojo.getBooks().add(b2);
    pojo.getHasKeyPks().add(hasKeyPk1);
    pojo.getHasKeyPks().add(hasKeyPk2);
    pojo.getBidirChildren().add(bidir1);
    pojo.getBidirChildren().add(bidir2);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), 1, 2);

    String bidir1Id = bidir1.getId();
    String bookId = b1.getId();
    Key hasKeyPk1Key = hasKeyPk1.getId();
    pojo.getBidirChildren().remove(bidir1);
    pojo.getBooks().remove(b1);
    pojo.getHasKeyPks().remove(hasKeyPk1);

    startEnd.start();
    em.merge(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    assertNotNull(pojo.getId());
    assertEquals(1, pojo.getBooks().size());
    assertEquals(1, pojo.getHasKeyPks().size());
    assertEquals(1, pojo.getBidirChildren().size());
    assertNotNull(bidir2.getId());
    assertNotNull(bidir2.getParent());
    assertNotNull(b2.getId());
    assertNotNull(hasKeyPk2.getId());
    assertEquals(bidir2.getId(), pojo.getBidirChildren().iterator().next().getId());
    assertEquals(hasKeyPk2.getId(), pojo.getHasKeyPks().iterator().next().getId());
    assertEquals(b2.getId(), pojo.getBooks().iterator().next().getId());

    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertNotNull(pojoEntity);
    assertEquals(4, pojoEntity.getProperties().size());
    assertEquals(Collections.singletonList(KeyFactory.stringToKey(bidir2.getId())), pojoEntity.getProperty("bidirChildren"));
    assertEquals(Collections.singletonList(KeyFactory.stringToKey(b2.getId())), pojoEntity.getProperty("books"));
    assertEquals(Collections.singletonList(hasKeyPk2.getId()), pojoEntity.getProperty("hasKeyPks"));

    startEnd.end();

    try {
      ds.get(KeyFactory.stringToKey(bidir1Id));
      fail("expected EntityNotFoundException");
    } catch (EntityNotFoundException enfe) {
      // good
    }
    try {
      ds.get(KeyFactory.stringToKey(bookId));
      fail("expected EntityNotFoundException");
    } catch (EntityNotFoundException enfe) {
      // good
    }
    try {
      ds.get(hasKeyPk1Key);
      fail("expected EntityNotFoundException");
    } catch (EntityNotFoundException enfe) {
      // good
    }
    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir2.getId()));
    assertNotNull(bidirChildEntity);
    assertEquals("yam2", bidirChildEntity.getProperty("childVal"));
    assertEquals(KeyFactory.stringToKey(bidir2.getId()), bidirChildEntity.getKey());
    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b2.getId()));
    assertNotNull(bookEntity);
    assertEquals("max", bookEntity.getProperty("author"));
    assertEquals("22333", bookEntity.getProperty("isbn"));
    assertEquals("another title", bookEntity.getProperty("title"));
    assertEquals(KeyFactory.stringToKey(b2.getId()), bookEntity.getKey());
    assertKeyParentEquals(pojo.getId(), bookEntity, b2.getId());

    Entity hasKeyPkEntity = ds.get(hasKeyPk2.getId());
    assertNotNull(hasKeyPkEntity);
    assertEquals("yar 2", hasKeyPkEntity.getProperty("str"));
    assertEquals(hasKeyPk2.getId(), hasKeyPkEntity.getKey());
    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getId());

    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertNotNull(parentEntity);
    assertEquals(4, parentEntity.getProperties().size());
    assertEquals("yar", parentEntity.getProperty("val"));
    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
    assertEquals(Collections.singletonList(bookEntity.getKey()), parentEntity.getProperty("books"));
    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));

    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), 1, 1);
  }

  void testChangeParent(HasOneToManyJPA pojo, HasOneToManyJPA pojo2,
                        StartEnd startEnd) throws Exception {
    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_not_allowed);
    Book b1 = new Book();
    pojo.getBooks().add(b1);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo2.getBooks().add(b1);
    try {
      em.persist(pojo2);
      startEnd.end();
      fail("expected exception");
    } catch (PersistenceException e) {
      rollbackTxn();
    }
  }

  void testNewParentNewChild_NamedKeyOnChild(HasOneToManyJPA pojo,
                                             StartEnd startEnd) throws Exception {
    Book b1 = new Book("named key");
    pojo.getBooks().add(b1);

    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b1.getId()));
    assertEquals("named key", bookEntity.getKey().getName());
  }

  void testAddAlreadyPersistedChildToParent_NoTxnDifferentEm(HasOneToManyJPA pojo) {
    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
    Book book = new Book();
    em.persist(book);
    em.close();
    em = emf.createEntityManager();
    pojo.getBooks().add(book);
    try {
      em.persist(pojo);
      em.close();
      fail("expected exception");
    } catch (PersistenceException e) {
      // good
    }

    assertEquals(0, countForClass(pojo.getClass()));
    assertEquals(1, countForClass(Book.class));
  }

  void testAddAlreadyPersistedChildToParent_NoTxnSameEm(HasOneToManyJPA pojo) {
    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
    Book book = new Book();
    em.persist(book);
    em.close();
    em = emf.createEntityManager();
    pojo.getBooks().add(book);
    try {
      em.persist(pojo);
      em.close();
      fail("expected exception");
    } catch (PersistenceException e) {
      // good
    }

    assertEquals(0, countForClass(pojo.getClass()));
    assertEquals(1, countForClass(Book.class));
  }

  void testFetchOfOneToManyParentWithKeyPk(HasOneToManyKeyPkJPA pojo,
                                           StartEnd startEnd) throws Exception {
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    assertEquals(0, pojo.getBooks().size());
    startEnd.end();
  }

  void testFetchOfOneToManyParentWithLongPk(HasOneToManyLongPkJPA pojo,
                                            StartEnd startEnd) throws Exception {
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    assertEquals(0, pojo.getBooks().size());
    startEnd.end();
  }

  void testFetchOfOneToManyParentWithUnencodedStringPk(HasOneToManyUnencodedStringPkJPA pojo,
                                                       StartEnd startEnd) throws Exception {
    pojo.setId("yar");
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    assertEquals(0, pojo.getBooks().size());
    startEnd.end();
  }

  void testAddChildToOneToManyParentWithLongPk(
      HasOneToManyLongPkJPA pojo, BidirectionalChildLongPkJPA bidirChild,
      StartEnd startEnd) throws Exception {
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    Book b = new Book();
    pojo.getBooks().add(b);
    pojo.getBidirChildren().add(bidirChild);
    bidirChild.setParent(pojo);
    startEnd.end();

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
    Entity pojoEntity = ds.get(KeyFactory.createKey(pojo.getClass().getSimpleName(), pojo.getId()));
    assertEquals(pojoEntity.getKey(), bookEntity.getParent());
    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
  }

  void testAddChildToOneToManyParentWithUnencodedStringPk(
      HasOneToManyUnencodedStringPkJPA pojo, BidirectionalChildUnencodedStringPkJPA bidirChild,
      StartEnd startEnd) throws Exception {
    pojo.setId("yar");
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    Book b = new Book();
    pojo.getBooks().add(b);
    pojo.getBidirChildren().add(bidirChild);
    startEnd.end();

    Entity bookEntity = ds.get(KeyFactory.stringToKey(b.getId()));
    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
    Entity pojoEntity = ds.get(KeyFactory.createKey(pojo.getClass().getSimpleName(), pojo.getId()));
    assertEquals(pojoEntity.getKey(), bookEntity.getParent());
    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
  }

  void testAddQueriedParentToBidirChild(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                                        StartEnd startEnd) throws Exception {
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
    bidir.setParent(pojo);
    pojo.getBidirChildren().add(bidir);
    em.persist(bidir);
    startEnd.end();

    startEnd.start();
    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
    assertEquals(1, pojo.getBidirChildren().size());
    startEnd.end();

    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
  }

  void testAddFetchedParentToBidirChild(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                                        StartEnd startEnd) throws Exception {
    startEnd.start();
    em.persist(pojo);
    startEnd.end();

    startEnd.start();
    pojo = em.find(pojo.getClass(), pojo.getId());
    bidir.setParent(pojo);
    pojo.getBidirChildren().add(bidir);
    em.persist(bidir);
    startEnd.end();

    startEnd.start();
    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
    assertEquals(1, pojo.getBidirChildren().size());
    startEnd.end();

    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
  }

  private static final class PutPolicy implements DatastoreServiceInterceptor.Policy {
    private final List<Object[]> putParamList = Utils.newArrayList();
    public void intercept(Object o, Method method, Object[] params) {
      if (method.getName().equals("put")) {
        putParamList.add(params);
      }
    }
  }

  PutPolicy setupPutPolicy(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                           StartEnd startEnd) throws Throwable {
    PutPolicy policy = new PutPolicy();
    DatastoreServiceInterceptor.install(getStoreManager(), policy);
    try {
      emf.close();
      switchDatasource(getEntityManagerFactoryName());
      Book book = new Book();
      pojo.getBooks().add(book);
      pojo.getBidirChildren().add(bidir);
      HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
      pojo.getHasKeyPks().add(hasKeyPk);

      startEnd.start();
      em.persist(pojo);
      startEnd.end();
      // 1 put for the parent, 3 puts for the children, 1 more put
      // to add the child keys back on the parent
      assertEquals(5, policy.putParamList.size());
      policy.putParamList.clear();
      return policy;
    } catch (Throwable t) {
      DatastoreServiceInterceptor.uninstall();
      throw t;
    }
  }

  void testOnlyOnePutOnChildUpdate(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                                   StartEnd startEnd) throws Throwable {
    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
    try {
      startEnd.start();
      pojo = em.find(pojo.getClass(), pojo.getId());
      pojo.getBooks().iterator().next().setTitle("some author");
      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
      startEnd.end();
    } finally {
      DatastoreServiceInterceptor.uninstall();
    }
    // 1 put for each child update
    assertEquals(3, policy.putParamList.size());
  }

  void testOnlyOneParentPutOnParentAndChildUpdate(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                                                  StartEnd startEnd) throws Throwable {
    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
    try {
      startEnd.start();
      pojo = em.find(pojo.getClass(), pojo.getId());
      pojo.setVal("another val");
      pojo.getBooks().iterator().next().setTitle("some author");
      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
      startEnd.end();
    } finally {
      DatastoreServiceInterceptor.uninstall();
    }
    // 1 put for the parent update, 1 put for each child update
    assertEquals(4, policy.putParamList.size());
  }

  void testOnlyOneParentPutOnChildDelete(HasOneToManyJPA pojo, BidirectionalChildJPA bidir,
                                         StartEnd startEnd,
                                         int expectedUpdatePuts) throws Throwable {
    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
    try {
      startEnd.start();
      pojo = em.find(pojo.getClass(), pojo.getId());
      pojo.setVal("another val");
      pojo.getBooks().clear();
      pojo.getBidirChildren().clear();
      pojo.getHasKeyPks().clear();
      startEnd.end();
    } finally {
      DatastoreServiceInterceptor.uninstall();
    }
    assertEquals(expectedUpdatePuts, policy.putParamList.size());
  }

  void assertCountsInDatastore(Class<? extends HasOneToManyJPA> pojoClass,
                               Class<? extends BidirectionalChildJPA> bidirClass,
                               int expectedParent, int expectedChildren) {
    assertEquals(pojoClass.getName(), expectedParent, countForClass(pojoClass));
    assertEquals(bidirClass.getName(), expectedChildren, countForClass(bidirClass));
    assertEquals(Book.class.getName(), expectedChildren, countForClass(Book.class));
    assertEquals(HasKeyPkJPA.class.getName(), expectedChildren, countForClass(HasKeyPkJPA.class));
  }

  Book newBook() {
    Book b = new Book(nextNamedKey());
    b.setAuthor("max");
    b.setIsbn("22333");
    b.setTitle("yam");
    return b;
  }
}