expand today placeholder in Dockerfile (#68)

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <com.coravy.hudson.plugins.github.GithubProjectProperty plugin="github@@1.25.1">
      <projectUrl>@(ci_scripts_repository)/</projectUrl>
      <displayName />
    </com.coravy.hudson.plugins.github.GithubProjectProperty>
    <com.sonyericsson.rebuild.RebuildSettings plugin="rebuild@@1.25">
      <autoRebuild>false</autoRebuild>
      <rebuildDisabled>false</rebuildDisabled>
    </com.sonyericsson.rebuild.RebuildSettings>
    <org.jenkinsci.plugins.requeuejob.RequeueJobProperty plugin="jobrequeue@@1.1">
      <requeueJob>true</requeueJob>
    </org.jenkinsci.plugins.requeuejob.RequeueJobProperty>
@(SNIPPET(
    'property_parameter-definition',
    ci_scripts_default_branch=ci_scripts_default_branch,
    default_repos_url=default_repos_url,
    supplemental_repos_url=supplemental_repos_url,
    use_connext_default=use_connext_default,
    disable_connext_static_default=disable_connext_static_default,
    disable_connext_dynamic_default=disable_connext_dynamic_default,
    use_osrf_connext_debs_default=use_osrf_connext_debs_default,
    use_fastrtps_default=use_fastrtps_default,
    use_opensplice_default=use_opensplice_default,
    cmake_build_type=cmake_build_type,
    ament_build_args_default=ament_build_args_default,
    ament_test_args_default=ament_test_args_default,
    enable_c_coverage_default=enable_c_coverage_default,
))@
  </properties>
  <scm class="hudson.plugins.git.GitSCM" plugin="git@@3.0.1">
    <configVersion>2</configVersion>
    <userRemoteConfigs>
      <hudson.plugins.git.UserRemoteConfig>
        <url>@(ci_scripts_repository)</url>
        <credentialsId>1c2004f6-2e00-425d-a421-2e1ba62ca7a7</credentialsId>
      </hudson.plugins.git.UserRemoteConfig>
    </userRemoteConfigs>
    <branches>
      <hudson.plugins.git.BranchSpec>
        <name>refs/heads/${CI_SCRIPTS_BRANCH}</name>
      </hudson.plugins.git.BranchSpec>
    </branches>
    <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
    <browser class="hudson.plugins.git.browser.GithubWeb">
      <url></url>
    </browser>
    <submoduleCfg class="list"/>
    <extensions>@
@[if os_name != 'windows']
      <hudson.plugins.git.extensions.impl.SubmoduleOption>
        <disableSubmodules>false</disableSubmodules>
        <recursiveSubmodules>true</recursiveSubmodules>
        <trackingSubmodules>false</trackingSubmodules>
        <reference/>
        <parentCredentials>false</parentCredentials>
      </hudson.plugins.git.extensions.impl.SubmoduleOption>
    @
@[end if]</extensions>
  </scm>
  <assignedNode>@(label_expression)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>@
@[if time_trigger_spec]
    <hudson.triggers.TimerTrigger>
      <spec>@(time_trigger_spec)</spec>
    </hudson.triggers.TimerTrigger>
  @
@[end if]</triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
    <hudson.plugins.groovy.SystemGroovy plugin="groovy@@2.0">
      <source class="hudson.plugins.groovy.StringSystemScriptSource">

          <sandbox>false</sandbox>
        </script>
      </source>
    </hudson.plugins.groovy.SystemGroovy>
    <hudson.tasks.@(shell_type)>
      <command>@
@[if os_name in ['linux', 'osx', 'linux-aarch64']]@
rm -rf ws workspace "work space"

echo "# BEGIN SECTION: Determine arguments"
export CI_ARGS="--do-venv --force-ansi-color --workspace-path $WORKSPACE"
if [ -n "${CI_BRANCH_TO_TEST+x}" ]; then
  export CI_ARGS="$CI_ARGS --test-branch $CI_BRANCH_TO_TEST"
fi
if [ "$CI_USE_WHITESPACE_IN_PATHS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --white-space-in sourcespace buildspace installspace workspace"
fi
if [ "$CI_USE_CONNEXT" = "true" ]; then
  export CI_ARGS="$CI_ARGS --connext"
fi
if [ "$CI_DISABLE_CONNEXT_STATIC" = "true" ]; then
  export CI_ARGS="$CI_ARGS --disable-connext-static"
fi
if [ "$CI_DISABLE_CONNEXT_DYNAMIC" = "true" ]; then
  export CI_ARGS="$CI_ARGS --disable-connext-dynamic"
fi
if [ "$CI_USE_OSRF_CONNEXT_DEBS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --osrf-connext-debs"
fi
if [ "$CI_USE_FASTRTPS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --fastrtps"
fi
if [ "$CI_USE_OPENSPLICE" = "true" ]; then
  export CI_ARGS="$CI_ARGS --opensplice"
fi
if [ -z "${CI_ROS2_REPOS_URL+x}" ]; then
  CI_ROS2_REPOS_URL="@default_repos_url"
fi
export CI_ARGS="$CI_ARGS --repo-file-url $CI_ROS2_REPOS_URL"
if [ -n "${CI_ROS2_SUPPLEMENTAL_REPOS_URL+x}" ]; then
  export CI_ARGS="$CI_ARGS --supplemental-repo-file-url $CI_ROS2_SUPPLEMENTAL_REPOS_URL"
fi
if [ "$CI_ISOLATED" = "true" ]; then
  export CI_ARGS="$CI_ARGS --isolated"
fi
if [ "${CI_CMAKE_BUILD_TYPE}" != "None" ]; then
  export CI_ARGS="$CI_ARGS --cmake-build-type $CI_CMAKE_BUILD_TYPE"
fi
if [ "$CI_ENABLE_C_COVERAGE" = "true" ]; then
  export CI_ARGS="$CI_ARGS --coverage"
fi
if [ -n "${CI_AMENT_BUILD_ARGS+x}" ]; then
  case $CI_AMENT_BUILD_ARGS in
    *-- )
      # delimiter is already appended
      ;;
    * )
      CI_AMENT_BUILD_ARGS="$CI_AMENT_BUILD_ARGS --"
      ;;
  esac
  export CI_ARGS="$CI_ARGS --ament-build-args $CI_AMENT_BUILD_ARGS"
fi
if [ -n "${CI_AMENT_TEST_ARGS+x}" ]; then
  case $CI_AMENT_TEST_ARGS in
    *-- )
      # delimiter is already appended
      ;;
    * )
      CI_AMENT_TEST_ARGS="$CI_AMENT_TEST_ARGS --"
      ;;
  esac
  export CI_ARGS="$CI_ARGS --ament-test-args $CI_AMENT_TEST_ARGS"
fi
@[if os_name in ['linux', 'linux-aarch64'] and turtlebot_demo]@
export CI_ARGS="$CI_ARGS --ros1-path /opt/ros/kinetic"
@[end if]@
echo "Using args: $CI_ARGS"
echo "# END SECTION"

@[if os_name in ['linux', 'linux-aarch64']]@
mkdir -p $HOME/.ccache
echo "# BEGIN SECTION: docker version"
docker version
echo "# END SECTION"
echo "# BEGIN SECTION: docker info"
docker info
echo "# END SECTION"
echo "# BEGIN SECTION: Inject date into Dockerfile"
sed -i "s/@@today_str/`date +%Y-%m-%d`/" linux_docker_resources/Dockerfile
echo "# END SECTION"
echo "# BEGIN SECTION: Build Dockerfile"
@[if os_name == 'linux-aarch64']@
sed -i 's+^FROM.*$+FROM aarch64/ubuntu:xenial+' linux_docker_resources/Dockerfile
sed -i 's+apt-get update+(apt-get update || true)+' linux_docker_resources/Dockerfile
docker build --build-arg PLATFORM=arm -t ros2_batch_ci_aarch64 linux_docker_resources
@[elif os_name == 'linux']@
@[  if turtlebot_demo]@
docker build --build-arg INSTALL_TURTLEBOT2_DEMO_DEPS=true -t ros2_batch_ci_turtlebot_demo linux_docker_resources
@[  else]@
docker build -t ros2_batch_ci linux_docker_resources
@[  end if]@
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]@
echo "# END SECTION"
echo "# BEGIN SECTION: Run Dockerfile"
@[if turtlebot_demo]@
export CONTAINER_NAME=ros2_batch_ci_turtlebot_demo
@[elif os_name == 'linux']@
export CONTAINER_NAME=ros2_batch_ci
@[elif os_name == 'linux-aarch64']@
export CONTAINER_NAME=ros2_batch_ci_aarch64
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]@
docker run --privileged -e UID=`id -u` -e GID=`id -g` -e CI_ARGS="$CI_ARGS" -e CCACHE_DIR=/home/rosbuild/.ccache -i -v `pwd`:/home/rosbuild/ci_scripts -v $HOME/.ccache:/home/rosbuild/.ccache $CONTAINER_NAME
echo "# END SECTION"
@[else]@
echo "# BEGIN SECTION: Run script"
/usr/local/bin/python3 -u run_ros2_batch.py $CI_ARGS
echo "# END SECTION"
@[end if]@
@[elif os_name == 'windows']@
rmdir /S /Q ws workspace "work space"

echo "# BEGIN SECTION: Determine arguments"
set "PATH=%PATH:"=%"
set "CI_ARGS=--force-ansi-color --workspace-path %WORKSPACE%"
if "%CI_BRANCH_TO_TEST%" NEQ "" (
  set "CI_ARGS=%CI_ARGS% --test-branch %CI_BRANCH_TO_TEST%"
)
if "%CI_USE_WHITESPACE_IN_PATHS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --white-space-in sourcespace buildspace installspace workspace"
)
if "%CI_USE_CONNEXT%" == "true" (
  set "CI_ARGS=%CI_ARGS% --connext"
)
if "%CI_DISABLE_CONNEXT_STATIC%" == "true" (
  set "CI_ARGS=%CI_ARGS% --disable-connext-static"
)
if "%CI_DISABLE_CONNEXT_DYNAMIC%" == "true" (
  set "CI_ARGS=%CI_ARGS% --disable-connext-dynamic"
)
if "%CI_USE_OSRF_CONNEXT_DEBS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --osrf-connext-debs"
)
if "%CI_USE_FASTRTPS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --fastrtps"
)
if "%CI_USE_OPENSPLICE%" == "true" (
  set "CI_ARGS=%CI_ARGS% --opensplice"
)
if "%CI_ROS2_REPOS_URL%" EQU "" (
  set "CI_ROS2_REPOS_URL=@default_repos_url"
)
set "CI_ARGS=%CI_ARGS% --repo-file-url %CI_ROS2_REPOS_URL%"
if "%CI_ROS2_SUPPLEMENTAL_REPOS_URL%" NEQ "" (
  set "CI_ARGS=%CI_ARGS% --supplemental-repo-file-url %CI_ROS2_SUPPLEMENTAL_REPOS_URL%"
)
if "%CI_ISOLATED%" == "true" (
  set "CI_ARGS=%CI_ARGS% --isolated"
)
if "%CI_CMAKE_BUILD_TYPE%" NEQ "None" (
  set "CI_ARGS=%CI_ARGS% --cmake-build-type %CI_CMAKE_BUILD_TYPE%"
)
if "%CI_ENABLE_C_COVERAGE%" == "true" (
  set "CI_ARGS=%CI_ARGS% --coverage"
)
if "%CI_AMENT_BUILD_ARGS%" NEQ "" (
  if "%CI_AMENT_BUILD_ARGS:~-2%" NEQ "--" (
    set "CI_AMENT_BUILD_ARGS=%CI_AMENT_BUILD_ARGS% --"
  )
  set "CI_ARGS=%CI_ARGS% --ament-build-args %CI_AMENT_BUILD_ARGS%"
)
if "%CI_AMENT_TEST_ARGS%" NEQ "" (
  if "%CI_AMENT_TEST_ARGS:~-2%" NEQ "--" (
    set "CI_AMENT_TEST_ARGS=%CI_AMENT_TEST_ARGS% --"
  )
  set "CI_ARGS=%CI_ARGS% --ament-test-args %CI_AMENT_TEST_ARGS%"
)
echo Using args: %CI_ARGS%
echo "# END SECTION"

echo "# BEGIN SECTION: Run script"
python -u run_ros2_batch.py %CI_ARGS%
echo "# END SECTION"
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]</command>
    </hudson.tasks.@(shell_type)>
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    os_name=os_name,
))@
@(SNIPPET(
    'publisher_cobertura',
))@
@(SNIPPET(
    'publisher_xunit',
))@
@[if mailer_recipients]@
    <hudson.tasks.Mailer plugin="mailer@@1.20">
      <recipients>@(mailer_recipients)</recipients>
      <dontNotifyEveryUnstableBuild>@(dont_notify_every_unstable_build)</dontNotifyEveryUnstableBuild>
      <sendToIndividuals>false</sendToIndividuals>
    </hudson.tasks.Mailer>
@[end if]@
  </publishers>
  <buildWrappers>
@[if build_timeout_mins]@
    <hudson.plugins.build__timeout.BuildTimeoutWrapper plugin="build-timeout@@1.18">
      <strategy class="hudson.plugins.build_timeout.impl.AbsoluteTimeOutStrategy">
        <timeoutMinutes>@(build_timeout_mins)</timeoutMinutes>
      </strategy>
      <operationList>
        <hudson.plugins.build__timeout.operations.AbortOperation />
      </operationList>
    </hudson.plugins.build__timeout.BuildTimeoutWrapper>
@[end if]@
    <hudson.plugins.timestamper.TimestamperBuildWrapper plugin="timestamper@@1.8.8" />
    <hudson.plugins.ansicolor.AnsiColorBuildWrapper plugin="ansicolor@@0.5.0">
      <colorMapName>xterm</colorMapName>
    </hudson.plugins.ansicolor.AnsiColorBuildWrapper>
@[if os_name != 'windows']@
    <com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper plugin="ssh-agent@@1.15">
      <credentialIds>
        <string>1c2004f6-2e00-425d-a421-2e1ba62ca7a7</string>
      </credentialIds>
      <ignoreMissing>false</ignoreMissing>
    </com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper>
@[end if]@
  </buildWrappers>
</project>

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <com.coravy.hudson.plugins.github.GithubProjectProperty plugin="github@@1.25.1">
      <projectUrl>@(ci_scripts_repository)/</projectUrl>
      <displayName />
    </com.coravy.hudson.plugins.github.GithubProjectProperty>
    <com.sonyericsson.rebuild.RebuildSettings plugin="rebuild@@1.25">
      <autoRebuild>false</autoRebuild>
      <rebuildDisabled>false</rebuildDisabled>
    </com.sonyericsson.rebuild.RebuildSettings>
    <org.jenkinsci.plugins.requeuejob.RequeueJobProperty plugin="jobrequeue@@1.1">
      <requeueJob>true</requeueJob>
    </org.jenkinsci.plugins.requeuejob.RequeueJobProperty>
@(SNIPPET(
    'property_parameter-definition',
    ci_scripts_default_branch=ci_scripts_default_branch,
    default_repos_url=default_repos_url,
    supplemental_repos_url=supplemental_repos_url,
    use_connext_default=use_connext_default,
    disable_connext_static_default=disable_connext_static_default,
    disable_connext_dynamic_default=disable_connext_dynamic_default,
    use_osrf_connext_debs_default=use_osrf_connext_debs_default,
    use_fastrtps_default=use_fastrtps_default,
    use_opensplice_default=use_opensplice_default,
    cmake_build_type=cmake_build_type,
    ament_build_args_default=ament_build_args_default,
    ament_test_args_default=ament_test_args_default,
    enable_c_coverage_default=enable_c_coverage_default,
))@
  </properties>
  <scm class="hudson.plugins.git.GitSCM" plugin="git@@3.0.1">
    <configVersion>2</configVersion>
    <userRemoteConfigs>
      <hudson.plugins.git.UserRemoteConfig>
        <url>@(ci_scripts_repository)</url>
        <credentialsId>1c2004f6-2e00-425d-a421-2e1ba62ca7a7</credentialsId>
      </hudson.plugins.git.UserRemoteConfig>
    </userRemoteConfigs>
    <branches>
      <hudson.plugins.git.BranchSpec>
        <name>refs/heads/${CI_SCRIPTS_BRANCH}</name>
      </hudson.plugins.git.BranchSpec>
    </branches>
    <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
    <browser class="hudson.plugins.git.browser.GithubWeb">
      <url></url>
    </browser>
    <submoduleCfg class="list"/>
    <extensions>@
@[if os_name != 'windows']
      <hudson.plugins.git.extensions.impl.SubmoduleOption>
        <disableSubmodules>false</disableSubmodules>
        <recursiveSubmodules>true</recursiveSubmodules>
        <trackingSubmodules>false</trackingSubmodules>
        <reference/>
        <parentCredentials>false</parentCredentials>
      </hudson.plugins.git.extensions.impl.SubmoduleOption>
    @
@[end if]</extensions>
  </scm>
  <assignedNode>@(label_expression)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>@
@[if time_trigger_spec]
    <hudson.triggers.TimerTrigger>
      <spec>@(time_trigger_spec)</spec>
    </hudson.triggers.TimerTrigger>
  @
@[end if]</triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
    <hudson.plugins.groovy.SystemGroovy plugin="groovy@@2.0">
      <source class="hudson.plugins.groovy.StringSystemScriptSource">

          <sandbox>false</sandbox>
        </script>
      </source>
    </hudson.plugins.groovy.SystemGroovy>
    <hudson.tasks.@(shell_type)>
      <command>@
@[if os_name in ['linux', 'osx', 'linux-aarch64']]@
rm -rf ws workspace "work space"

echo "# BEGIN SECTION: Determine arguments"
export CI_ARGS="--do-venv --force-ansi-color --workspace-path $WORKSPACE"
if [ -n "${CI_BRANCH_TO_TEST+x}" ]; then
  export CI_ARGS="$CI_ARGS --test-branch $CI_BRANCH_TO_TEST"
fi
if [ "$CI_USE_WHITESPACE_IN_PATHS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --white-space-in sourcespace buildspace installspace workspace"
fi
if [ "$CI_USE_CONNEXT" = "true" ]; then
  export CI_ARGS="$CI_ARGS --connext"
fi
if [ "$CI_DISABLE_CONNEXT_STATIC" = "true" ]; then
  export CI_ARGS="$CI_ARGS --disable-connext-static"
fi
if [ "$CI_DISABLE_CONNEXT_DYNAMIC" = "true" ]; then
  export CI_ARGS="$CI_ARGS --disable-connext-dynamic"
fi
if [ "$CI_USE_OSRF_CONNEXT_DEBS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --osrf-connext-debs"
fi
if [ "$CI_USE_FASTRTPS" = "true" ]; then
  export CI_ARGS="$CI_ARGS --fastrtps"
fi
if [ "$CI_USE_OPENSPLICE" = "true" ]; then
  export CI_ARGS="$CI_ARGS --opensplice"
fi
if [ -z "${CI_ROS2_REPOS_URL+x}" ]; then
  CI_ROS2_REPOS_URL="@default_repos_url"
fi
export CI_ARGS="$CI_ARGS --repo-file-url $CI_ROS2_REPOS_URL"
if [ -n "${CI_ROS2_SUPPLEMENTAL_REPOS_URL+x}" ]; then
  export CI_ARGS="$CI_ARGS --supplemental-repo-file-url $CI_ROS2_SUPPLEMENTAL_REPOS_URL"
fi
if [ "$CI_ISOLATED" = "true" ]; then
  export CI_ARGS="$CI_ARGS --isolated"
fi
if [ "${CI_CMAKE_BUILD_TYPE}" != "None" ]; then
  export CI_ARGS="$CI_ARGS --cmake-build-type $CI_CMAKE_BUILD_TYPE"
fi
if [ "$CI_ENABLE_C_COVERAGE" = "true" ]; then
  export CI_ARGS="$CI_ARGS --coverage"
fi
if [ -n "${CI_AMENT_BUILD_ARGS+x}" ]; then
  case $CI_AMENT_BUILD_ARGS in
    *-- )
      # delimiter is already appended
      ;;
    * )
      CI_AMENT_BUILD_ARGS="$CI_AMENT_BUILD_ARGS --"
      ;;
  esac
  export CI_ARGS="$CI_ARGS --ament-build-args $CI_AMENT_BUILD_ARGS"
fi
if [ -n "${CI_AMENT_TEST_ARGS+x}" ]; then
  case $CI_AMENT_TEST_ARGS in
    *-- )
      # delimiter is already appended
      ;;
    * )
      CI_AMENT_TEST_ARGS="$CI_AMENT_TEST_ARGS --"
      ;;
  esac
  export CI_ARGS="$CI_ARGS --ament-test-args $CI_AMENT_TEST_ARGS"
fi
@[if os_name in ['linux', 'linux-aarch64'] and turtlebot_demo]@
export CI_ARGS="$CI_ARGS --ros1-path /opt/ros/kinetic"
@[end if]@
echo "Using args: $CI_ARGS"
echo "# END SECTION"

@[if os_name in ['linux', 'linux-aarch64']]@
mkdir -p $HOME/.ccache
echo "# BEGIN SECTION: docker version"
docker version
echo "# END SECTION"
echo "# BEGIN SECTION: docker info"
docker info
echo "# END SECTION"
echo "# BEGIN SECTION: Build Dockerfile"
@[if os_name == 'linux-aarch64']@
sed -i 's+^FROM.*$+FROM aarch64/ubuntu:xenial+' linux_docker_resources/Dockerfile
sed -i 's+apt-get update+(apt-get update || true)+' linux_docker_resources/Dockerfile
docker build --build-arg PLATFORM=arm -t ros2_batch_ci_aarch64 linux_docker_resources
@[elif os_name == 'linux']@
@[  if turtlebot_demo]@
docker build --build-arg INSTALL_TURTLEBOT2_DEMO_DEPS=true -t ros2_batch_ci_turtlebot_demo linux_docker_resources
@[  else]@
docker build -t ros2_batch_ci linux_docker_resources
@[  end if]@
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]@
echo "# END SECTION"
echo "# BEGIN SECTION: Run Dockerfile"
@[if turtlebot_demo]@
export CONTAINER_NAME=ros2_batch_ci_turtlebot_demo
@[elif os_name == 'linux']@
export CONTAINER_NAME=ros2_batch_ci
@[elif os_name == 'linux-aarch64']@
export CONTAINER_NAME=ros2_batch_ci_aarch64
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]@
docker run --privileged -e UID=`id -u` -e GID=`id -g` -e CI_ARGS="$CI_ARGS" -e CCACHE_DIR=/home/rosbuild/.ccache -i -v `pwd`:/home/rosbuild/ci_scripts -v $HOME/.ccache:/home/rosbuild/.ccache $CONTAINER_NAME
echo "# END SECTION"
@[else]@
echo "# BEGIN SECTION: Run script"
/usr/local/bin/python3 -u run_ros2_batch.py $CI_ARGS
echo "# END SECTION"
@[end if]@
@[elif os_name == 'windows']@
rmdir /S /Q ws workspace "work space"

echo "# BEGIN SECTION: Determine arguments"
set "PATH=%PATH:"=%"
set "CI_ARGS=--force-ansi-color --workspace-path %WORKSPACE%"
if "%CI_BRANCH_TO_TEST%" NEQ "" (
  set "CI_ARGS=%CI_ARGS% --test-branch %CI_BRANCH_TO_TEST%"
)
if "%CI_USE_WHITESPACE_IN_PATHS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --white-space-in sourcespace buildspace installspace workspace"
)
if "%CI_USE_CONNEXT%" == "true" (
  set "CI_ARGS=%CI_ARGS% --connext"
)
if "%CI_DISABLE_CONNEXT_STATIC%" == "true" (
  set "CI_ARGS=%CI_ARGS% --disable-connext-static"
)
if "%CI_DISABLE_CONNEXT_DYNAMIC%" == "true" (
  set "CI_ARGS=%CI_ARGS% --disable-connext-dynamic"
)
if "%CI_USE_OSRF_CONNEXT_DEBS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --osrf-connext-debs"
)
if "%CI_USE_FASTRTPS%" == "true" (
  set "CI_ARGS=%CI_ARGS% --fastrtps"
)
if "%CI_USE_OPENSPLICE%" == "true" (
  set "CI_ARGS=%CI_ARGS% --opensplice"
)
if "%CI_ROS2_REPOS_URL%" EQU "" (
  set "CI_ROS2_REPOS_URL=@default_repos_url"
)
set "CI_ARGS=%CI_ARGS% --repo-file-url %CI_ROS2_REPOS_URL%"
if "%CI_ROS2_SUPPLEMENTAL_REPOS_URL%" NEQ "" (
  set "CI_ARGS=%CI_ARGS% --supplemental-repo-file-url %CI_ROS2_SUPPLEMENTAL_REPOS_URL%"
)
if "%CI_ISOLATED%" == "true" (
  set "CI_ARGS=%CI_ARGS% --isolated"
)
if "%CI_CMAKE_BUILD_TYPE%" NEQ "None" (
  set "CI_ARGS=%CI_ARGS% --cmake-build-type %CI_CMAKE_BUILD_TYPE%"
)
if "%CI_ENABLE_C_COVERAGE%" == "true" (
  set "CI_ARGS=%CI_ARGS% --coverage"
)
if "%CI_AMENT_BUILD_ARGS%" NEQ "" (
  if "%CI_AMENT_BUILD_ARGS:~-2%" NEQ "--" (
    set "CI_AMENT_BUILD_ARGS=%CI_AMENT_BUILD_ARGS% --"
  )
  set "CI_ARGS=%CI_ARGS% --ament-build-args %CI_AMENT_BUILD_ARGS%"
)
if "%CI_AMENT_TEST_ARGS%" NEQ "" (
  if "%CI_AMENT_TEST_ARGS:~-2%" NEQ "--" (
    set "CI_AMENT_TEST_ARGS=%CI_AMENT_TEST_ARGS% --"
  )
  set "CI_ARGS=%CI_ARGS% --ament-test-args %CI_AMENT_TEST_ARGS%"
)
echo Using args: %CI_ARGS%
echo "# END SECTION"

echo "# BEGIN SECTION: Run script"
python -u run_ros2_batch.py %CI_ARGS%
echo "# END SECTION"
@[else]@
@{ assert 'Unknown os_name: ' + os_name }@
@[end if]</command>
    </hudson.tasks.@(shell_type)>
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    os_name=os_name,
))@
@(SNIPPET(
    'publisher_cobertura',
))@
@(SNIPPET(
    'publisher_xunit',
))@
@[if mailer_recipients]@
    <hudson.tasks.Mailer plugin="mailer@@1.20">
      <recipients>@(mailer_recipients)</recipients>
      <dontNotifyEveryUnstableBuild>@(dont_notify_every_unstable_build)</dontNotifyEveryUnstableBuild>
      <sendToIndividuals>false</sendToIndividuals>
    </hudson.tasks.Mailer>
@[end if]@
  </publishers>
  <buildWrappers>
@[if build_timeout_mins]@
    <hudson.plugins.build__timeout.BuildTimeoutWrapper plugin="build-timeout@@1.18">
      <strategy class="hudson.plugins.build_timeout.impl.AbsoluteTimeOutStrategy">
        <timeoutMinutes>@(build_timeout_mins)</timeoutMinutes>
      </strategy>
      <operationList>
        <hudson.plugins.build__timeout.operations.AbortOperation />
      </operationList>
    </hudson.plugins.build__timeout.BuildTimeoutWrapper>
@[end if]@
    <hudson.plugins.timestamper.TimestamperBuildWrapper plugin="timestamper@@1.8.8" />
    <hudson.plugins.ansicolor.AnsiColorBuildWrapper plugin="ansicolor@@0.5.0">
      <colorMapName>xterm</colorMapName>
    </hudson.plugins.ansicolor.AnsiColorBuildWrapper>
@[if os_name != 'windows']@
    <com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper plugin="ssh-agent@@1.15">
      <credentialIds>
        <string>1c2004f6-2e00-425d-a421-2e1ba62ca7a7</string>
      </credentialIds>
      <ignoreMissing>false</ignoreMissing>
    </com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper>
@[end if]@
  </buildWrappers>
</project>


use same os_codename to generate Dockerfile for dev jobs

# generated from @template_name

FROM ubuntu:@os_code_name
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

VOLUME ["/var/cache/apt/archives"]

ENV DEBIAN_FRONTEND noninteractive
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV TZ @timezone

RUN useradd -u @uid -m buildfarm

@(TEMPLATE(
    'snippet/add_distribution_repositories.Dockerfile.em',
    distribution_repository_keys=distribution_repository_keys,
    distribution_repository_urls=distribution_repository_urls,
    os_code_name=os_code_name,
    add_source=False,
))@

@(TEMPLATE(
    'snippet/add_wrapper_scripts.Dockerfile.em',
    wrapper_scripts=wrapper_scripts,
))@

# automatic invalidation once every day
RUN echo "@today_str"

@(TEMPLATE(
    'snippet/install_python3.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@

RUN python3 -u /tmp/wrapper_scripts/apt-get.py update-and-install -q -y git python3-apt python3-catkin-pkg python3-empy python3-rosdep python3-rosdistro wget

# always invalidate to actually have the latest rosdep state
RUN echo "@now_str"
ENV ROSDISTRO_INDEX_URL @rosdistro_index_url

@(TEMPLATE(
    'snippet/rosdep_init.Dockerfile.em',
    custom_rosdep_urls=custom_rosdep_urls,
))@

USER buildfarm

ENTRYPOINT ["sh", "-c"]
@{
cmds = [
'rosdep update',
]
workspace_root = '/tmp/catkin_workspace'
if prerelease_overlay:
    workspace_root += ' /tmp/catkin_workspace_overlay'
cmd = \
    'PYTHONPATH=/tmp/ros_buildfarm:$PYTHONPATH python3 -u' + \
    ' /tmp/ros_buildfarm/scripts/devel/create_devel_task_generator.py' + \
    ' --rosdistro-name ' + rosdistro_name + \
    ' --workspace-root ' + workspace_root + \
    ' --os-name ' + os_name + \
    ' --os-code-name ' + os_code_name + \
    ' --arch ' + arch + \
    ' --distribution-repository-urls ' + ' '.join(distribution_repository_urls) + \
    ' --distribution-repository-key-files ' + ' ' .join(['/tmp/keys/%d.key' % i for i in range(len(distribution_repository_keys))])
cmds += [
    cmd +
    ' --dockerfile-dir /tmp/docker_build_and_install',
    cmd +
    ' --dockerfile-dir /tmp/docker_build_and_test' +
    ' --testing',
]
}@
CMD ["@(' && '.join(cmds))"]

# generated from @template_name

FROM ubuntu:trusty
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

VOLUME ["/var/cache/apt/archives"]

ENV DEBIAN_FRONTEND noninteractive
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV TZ @timezone

RUN useradd -u @uid -m buildfarm

@(TEMPLATE(
    'snippet/add_distribution_repositories.Dockerfile.em',
    distribution_repository_keys=distribution_repository_keys,
    distribution_repository_urls=distribution_repository_urls,
    os_code_name='trusty',
    add_source=False,
))@

@(TEMPLATE(
    'snippet/add_wrapper_scripts.Dockerfile.em',
    wrapper_scripts=wrapper_scripts,
))@

# automatic invalidation once every day
RUN echo "@today_str"

RUN python3 -u /tmp/wrapper_scripts/apt-get.py update-and-install -q -y git python3-apt python3-catkin-pkg python3-empy python3-rosdep python3-rosdistro wget

# always invalidate to actually have the latest rosdep state
RUN echo "@now_str"
ENV ROSDISTRO_INDEX_URL @rosdistro_index_url

@(TEMPLATE(
    'snippet/rosdep_init.Dockerfile.em',
    custom_rosdep_urls=custom_rosdep_urls,
))@

USER buildfarm

ENTRYPOINT ["sh", "-c"]
@{
cmds = [
'rosdep update',
]
workspace_root = '/tmp/catkin_workspace'
if prerelease_overlay:
    workspace_root += ' /tmp/catkin_workspace_overlay'
cmd = \
    'PYTHONPATH=/tmp/ros_buildfarm:$PYTHONPATH python3 -u' + \
    ' /tmp/ros_buildfarm/scripts/devel/create_devel_task_generator.py' + \
    ' --rosdistro-name ' + rosdistro_name + \
    ' --workspace-root ' + workspace_root + \
    ' --os-name ' + os_name + \
    ' --os-code-name ' + os_code_name + \
    ' --arch ' + arch + \
    ' --distribution-repository-urls ' + ' '.join(distribution_repository_urls) + \
    ' --distribution-repository-key-files ' + ' ' .join(['/tmp/keys/%d.key' % i for i in range(len(distribution_repository_keys))])
cmds += [
    cmd +
    ' --dockerfile-dir /tmp/docker_build_and_install',
    cmd +
    ' --dockerfile-dir /tmp/docker_build_and_test' +
    ' --testing',
]
}@
CMD ["@(' && '.join(cmds))"]


add callback to constructor

add callback to constructor

if (typeof(std) === "undefined") /** @namespace */ std = {};

/* Map data structure
 * Constructor:
 *   e.g. X = new std.persistentMap('mapTest', function(exist){...});
 *
 * APIs:
 *   set(key,value)     : Write element to memory
 *   copy(map)          : Write all elements in given associative array to this map (in memory)
 *                          e.g. M = {'a':'x','b':'y'};  X.copy(M);
 *   get(key)           : Read element with given key from memory, return the value
 *   get(key,cb)        : Read element with given key from memory first, if not exists, read
 *                        from backend storage then. Callback: cb(success, value)
 *   erase(key)         : Erase element from memory
 *   clear()            : Clear content in memory
 *   flush(cb)          : Flush changes to backend storage. Callback: cb(success)
 *   retrieve(keys,cb)  : Retrieve elements with given list of keys from backend to memory
 *                        Callback: cb(success)
 *   restore(cb)        : Restore all elements from backend to memory. Callback: cb(success)
 *   reset(cb)          : Clear content in both memory and backend. Callback: cb(success)
 *   name()             : Return name of the map
 *   data()             : Return all elements in memory, as an associative array
 *   keys()             : Return all keys in both memory and backend, as an array.
 *   size()             : Return number of elements in memory and backend
 *   empty()            : Return true if map is emty, false otherwise
 *   contains(key)      : Return true if the map contains the key, false otherwise
 *   isResident(key)    : Return true if the key is resident in memory, false otherwise
 */

std.persistentMap = function(name, cb)
{
    this._type = 'map';
    this._name = name;
    this._mapName = keyName(this._type,this._name);
    this._data = {};
    this._keys = {};
    this._dirtyKeys = {};
    this._keys_list = {};
    this._init(cb);
};

std.persistentMap.prototype._init = function(cb)
{
    system.storageRead(this._mapName, std.core.bind(this._initCommit, this, cb));
};

std.persistentMap.prototype._initCommit = function(cb, exist, val)
{
    if (!exist)
        system.print('This is a new map')
    else {
        this._keys = StringtoKeys(val[this._mapName]);
        this._keys_list = this._keys;
        system.print('Map exist, restore keys')
    }
    cb(exist);
};

std.persistentMap.prototype.set = function(key, value)
{
    this._data[key]=value;
    this._dirtyKeys[key] = 1;
    this._keys[key] = 1;
};

std.persistentMap.prototype.copy = function(map)
{
    for(key in map)
        this.set(key, map[key]);
}

std.persistentMap.prototype.get = function()
{
    if (arguments.length == 1) {
        key = arguments[0];
        return this._data[key];
    }
    else if (arguments.length == 2) {
        key = arguments[0];
        cb = arguments[1];
        if(key in this._data)
            cb(true, this._data[key]);
        else
            system.storageRead(keyName(this._mapName, key), std.core.bind(this._getCommit, this, key, cb));
    }
    else {
        system.print('API *get* usage: get(key) or get(key, cb)');
        return false;
    }
};

std.persistentMap.prototype._getCommit = function(key, cb, success, val)
{
    if(!success)
        system.print('key does not exist');
    else
        this._data[key]=val[keyName(this._mapName, key)];
    cb(success, this._data[key]);
};

std.persistentMap.prototype.erase = function(key)
{
    delete this._data[key];
    delete this._keys[key];
    if (key in this._keys_list)
        this._dirtyKeys[key] = 0;
    else
        delete this._dirtyKeys[key];
};

std.persistentMap.prototype.clear = function()
{
    for (key in this._data)
        this.erase(key);
};

std.persistentMap.prototype.flush = function(cb)
{
    system.storageBeginTransaction();
    for (key in this._dirtyKeys) {
        if(this._dirtyKeys[key]==1)
            system.storageWrite(keyName(this._mapName, key), this._data[key]);
        if(this._dirtyKeys[key]==0)
            system.storageErase(keyName(this._mapName, key));
    }
    system.storageWrite(this._mapName, KeystoString(this._keys));
    system.storageCommit(std.core.bind(this._flushCommit, this, cb));
};

std.persistentMap.prototype._flushCommit = function(cb, success)
{
    if (!success)
        system.print('Flush fails');
    else {
        this._dirtyKeys={};
        this._keys_list = this._keys;
    }
    cb(success);
};

std.persistentMap.prototype.retrieve = function(keys, cb)
{
    system.storageBeginTransaction();
    for(i=0; i<keys.length; i++)
        system.storageRead(keyName(this._mapName, keys[i]));
    system.storageCommit(std.core.bind(this._retrieveCommit, this, keys, cb));
};

std.persistentMap.prototype._retrieveCommit = function(keys, cb, success, val)
{
    if(!success)
        system.print('Retrieve fails');
    else
        for (i=0; i<keys.length; i++)
            this._data[keys[i]] = val[keyName(this._mapName, keys[i])];
    cb(success);
};

std.persistentMap.prototype.restore = function(cb)
{
    system.storageRead(this._mapName, std.core.bind(this._restore, this, cb));
};

std.persistentMap.prototype._restore = function(cb, success, val)
{
    if (!success) {
        system.print('Map does not exist');
        cb(success);
    }
    else {
        this._keys = {};
        list = val[this._mapName].split(',');
        if(list.length>0) {
            system.storageBeginTransaction();
            for (i=0; i<list.length; i++) {
                system.storageRead(keyName(this._mapName, list[i]));
                this._keys[list[i]] = 1;
            }
            system.storageCommit(std.core.bind(this._restoreCommit, this, cb));
        }
        else {
            system.print('Map is empty');
            cb(success)
        }
    }
};

std.persistentMap.prototype._restoreCommit = function(cb, success, val)
{
    if (!success)
        system.print('Restore fails');
    else {
        for (key in val)
            this._data[key.split(':')[2]]=val[key];
        this._dirtyKeys = {};
        this._keys_list = this._keys;
    }
    cb(success);
};

std.persistentMap.prototype.reset = function(cb)
{
    system.storageBeginTransaction();
    system.storageErase(this._mapName);
    for(key in this._keys)
        system.storageErase(keyName(this._mapName, key));
    for(key in this._dirtyKeys)
        if(this._dirtyKeys[key]==0)
            system.storageErase(keyName(this._mapName, key));
    system.storageCommit(std.core.bind(this._resetCommit, this, cb));
};

std.persistentMap.prototype._resetCommit = function(cb, success)
{
    if (!success)
        system.print('reset fails');
    else {
        this._data = {};
        this._keys = {};
        this._dirtyKeys = {};
        this._keys_list = {};
        system.print('map reset');
    }
    cb(success);
};

std.persistentMap.prototype.contains = function(key)
{
    if (key in this._keys)
        return true;
    else
        return false;
};

std.persistentMap.prototype.isResident = function(key)
{
    if (key in this._data)
        return true;
    else
        return false;
};

std.persistentMap.prototype.type = function()
{
    return this._type;
};

std.persistentMap.prototype.name = function()
{
    return this._name;
};

std.persistentMap.prototype.data = function()
{
    return this._data;
};

std.persistentMap.prototype.dirtyKeys = function()
{
    return KeystoList(this._dirtyKeys);
};

std.persistentMap.prototype.keys = function()
{
    return KeystoList(this._keys);
};

std.persistentMap.prototype.size = function()
{
    size = 0;
    for (key in this._keys)
        size++;
    return size;
};

std.persistentMap.prototype.empty = function()
{
    for (key in this._keys)
        return false;
    return true;
};

var keyName = function(name, subname) {
    return name + ':' + subname;
};

var KeystoString = function(keys) {
    str='';
    for (key in keys)
        str = str+','+key;
    return str.substr(1);
};

var KeystoList = function(keys) {
    list = [];
    for (key in keys)
        list.push(key);
    return list;
};

var StringtoKeys = function(str) {
    list = str.split(',');
    keys = {};
    for (i=0; i<list.length; i++)
        keys[list[i]] = 1;
    return keys;
};

if (typeof(std) === "undefined") /** @namespace */ std = {};

/* Map data structure
 * Constructor:
 *   e.g. X = new std.persistentMap('mapTest');
 *
 * APIs:
 *   set(key,value)     : Write element to memory
 *   copy(map)          : Write all elements in given associative array to this map (in memory)
 *                          e.g. M = {'a':'x','b':'y'};  X.copy(M);
 *   get(key)           : Read element with given key from memory, return the value
 *   get(key,cb)        : Read element with given key from memory first, if not exists, read
 *                        from backend storage then. Callback: cb(success, value)
 *   erase(key)         : Erase element from memory
 *   clear()            : Clear content in memory
 *   flush(cb)          : Flush changes to backend storage. Callback: cb(success)
 *   retrieve(keys,cb)  : Retrieve elements with given list of keys from backend to memory
 *                        Callback: cb(success)
 *   restore(cb)        : Restore all elements from backend to memory. Callback: cb(success)
 *   reset(cb)          : Clear content in both memory and backend. Callback: cb(success)
 *   name()             : Return name of the map
 *   data()             : Return all elements in memory, as an associative array
 *   keys()             : Return all keys in both memory and backend, as an array.
 *   size()             : Return number of elements in memory and backend
 *   empty()            : Return true if map is emty, false otherwise
 *   contains(key)      : Return true if the map contains the key, false otherwise
 *   isResident(key)    : Return true if the key is resident in memory, false otherwise
 */

std.persistentMap = function(name)
{
    this._type = 'map';
    this._name = name;
    this._mapName = keyName(this._type,this._name);
    this._dirtyKeys = {};
    this._data = {};
    this._keys = {};
    this._keys_list = {};
    this._init(function(success){});
};

std.persistentMap.prototype._init = function(cb)
{
    system.storageRead(this._mapName, std.core.bind(this._initCommit, this, cb));
};

std.persistentMap.prototype._initCommit = function(cb, success, val)
{
    if (!success)
        system.print('This is a new map')
    else {
        this._keys = StringtoKeys(val[this._mapName]);
        this._keys_list = this._keys;
        system.print('Map exist, restore keys')
    }
    cb(success);
};

std.persistentMap.prototype.set = function(key,value)
{
    this._data[key]=value;
    this._dirtyKeys[key] = 1;
    this._keys[key] = 1;
};

std.persistentMap.prototype.copy = function(map)
{
    for(key in map)
        this.set(key, map[key]);
}

std.persistentMap.prototype.get = function()
{
    if (arguments.length == 1) {
        key = arguments[0];
        return this._data[key];
    }
    else if (arguments.length == 2) {
        key = arguments[0];
        cb = arguments[1];
        if(key in this._data)
            cb(true, this._data[key]);
        else
            system.storageRead(keyName(this._mapName, key), std.core.bind(this._getCommit, this, key, cb));
    }
    else {
        system.print('API *get* usage: get(key) or get(key, cb)');
        return false;
    }
};

std.persistentMap.prototype._getCommit = function(key, cb, success, val)
{
    if(!success)
        system.print('key does not exist');
    else
        this._data[key]=val[keyName(this._mapName, key)];
    cb(success, this._data[key]);
};

std.persistentMap.prototype.erase = function(key)
{
    delete this._data[key];
    delete this._keys[key];
    if (key in this._keys_list)
        this._dirtyKeys[key] = 0;
    else
        delete this._dirtyKeys[key];
};

std.persistentMap.prototype.clear = function()
{
    for (key in this._data)
        this.erase(key);
};

std.persistentMap.prototype.flush = function(cb)
{
    system.storageBeginTransaction();
    for (key in this._dirtyKeys) {
        if(this._dirtyKeys[key]==1)
            system.storageWrite(keyName(this._mapName, key), this._data[key]);
        if(this._dirtyKeys[key]==0)
            system.storageErase(keyName(this._mapName, key));
    }
    system.storageWrite(this._mapName, KeystoString(this._keys));
    system.storageCommit(std.core.bind(this._flushCommit, this, cb));
};

std.persistentMap.prototype._flushCommit = function(cb, success)
{
    if (!success)
        system.print('Flush fails');
    else {
        this._dirtyKeys={};
        this._keys_list = this._keys;
    }
    cb(success);
};

std.persistentMap.prototype.retrieve = function(keys, cb)
{
    system.storageBeginTransaction();
    for(i=0; i<keys.length; i++)
        system.storageRead(keyName(this._mapName, keys[i]));
    system.storageCommit(std.core.bind(this._retrieveCommit, this, keys, cb));
};

std.persistentMap.prototype._retrieveCommit = function(keys, cb, success, val)
{
    if(!success)
        system.print('Retrieve fails');
    else
        for (i=0; i<keys.length; i++)
            this._data[keys[i]] = val[keyName(this._mapName, keys[i])];
    cb(success);
};

std.persistentMap.prototype.restore = function(cb)
{
    system.storageRead(this._mapName, std.core.bind(this._restore, this, cb));
};

std.persistentMap.prototype._restore = function(cb, success, val)
{
    if (!success) {
        system.print('Map does not exist');
        cb(success);
    }
    else {
        this._keys = {};
        list = val[this._mapName].split(',');
        if(list.length>0) {
            system.storageBeginTransaction();
            for (i=0; i<list.length; i++) {
                system.storageRead(keyName(this._mapName, list[i]));
                this._keys[list[i]] = 1;
            }
            system.storageCommit(std.core.bind(this._restoreCommit, this, cb));
        }
        else {
            system.print('Map is empty');
            cb(success)
        }
    }
};

std.persistentMap.prototype._restoreCommit = function(cb, success, val)
{
    if (!success)
        system.print('Restore fails');
    else {
        for (key in val)
            this._data[key.split(':')[2]]=val[key];
        this._dirtyKeys = {};
        this._keys_list = this._keys;
    }
    cb(success);
};

std.persistentMap.prototype.reset = function(cb)
{
    system.storageBeginTransaction();
    system.storageErase(this._mapName);
    for(key in this._keys)
        system.storageErase(keyName(this._mapName, key));
    for(key in this._dirtyKeys)
        if(this._dirtyKeys[key]==0)
            system.storageErase(keyName(this._mapName, key));
    system.storageCommit(std.core.bind(this._resetCommit, this, cb));
};

std.persistentMap.prototype._resetCommit = function(cb, success)
{
    if (!success)
        system.print('reset fails');
    else {
        this._data = {};
        this._keys = {};
        this._dirtyKeys = {};
        this._keys_list = {};
        system.print('map reset');
    }
    cb(success);
};

std.persistentMap.prototype.contains = function(key)
{
    if (key in this._keys)
        return true;
    else
        return false;
};

std.persistentMap.prototype.isResident = function(key)
{
    if (key in this._data)
        return true;
    else
        return false;
};

std.persistentMap.prototype.type = function()
{
    return this._type;
};

std.persistentMap.prototype.name = function()
{
    return this._name;
};

std.persistentMap.prototype.data = function()
{
    return this._data;
};

std.persistentMap.prototype.dirtyKeys = function()
{
    return KeystoList(this._dirtyKeys);
};

std.persistentMap.prototype.keys = function()
{
    return KeystoList(this._keys);
};

std.persistentMap.prototype.size = function()
{
    size = 0;
    for (key in this._keys)
        size++;
    return size;
};

std.persistentMap.prototype.empty = function()
{
    for (key in this._keys)
        return false;
    return true;
};

var keyName = function(name, subname) {
    return name + ':' + subname;
};

var KeystoString = function(keys) {
    str='';
    for (key in keys)
        str = str+','+key;
    return str.substr(1);
};

var KeystoList = function(keys) {
    list = [];
    for (key in keys)
        list.push(key);
    return list;
};

var StringtoKeys = function(str) {
    list = str.split(',');
    keys = {};
    for (i=0; i<list.length; i++)
        keys[list[i]] = 1;
    return keys;
};


ensure exporting variables before invoking python (else they might not be available, i.e. in udev env)

#!/usr/bin/env sh
# generated from catkin/cmake/em/setup.sh.em

# Sets various the enviroment variables and sources additional enviroment hooks.
# The output depends on the CMAKE_PREFIX_PATH and the path of this workspace.
# It tries it's best to undo changes from a previously sourced setup file before.
# Supported command line options:
# --extend: skips the undoing of changes from a previously sourced setup file
# Supported enviroment variables:
# _CATKIN_SETUP_NO_RECURSION: skips it's own recursion (when invoked by another setup file)

# environment at generation time
# CURRENT_CMAKE_PREFIX_PATH="@CMAKE_PREFIX_PATH"
# CURRENT_WORKSPACE="@SETUP_DIR"

# remember type of shell if not already set
if [ -z "$CATKIN_SHELL" ]; then
  CATKIN_SHELL=sh
fi

# decide which commands to perform
DO_CLEAN=1
IS_RECURSIVE=0
if [ "$_CATKIN_SETUP_NO_RECURSION" = "1" ]; then
  DO_CLEAN=0
  IS_RECURSIVE=1
elif [ $# -eq 1 -a "$1" = "--extend" ]; then
  DO_CLEAN=0
fi

# folder of workspace prepended to CMAKE_PREFIX_PATH
CMAKE_PREFIX_PATH_dir=""
# include folder of workspace prepended to CPATH
CPATH_dir=/include
# lib folder of workspace prepended to (DY)LD_LIBRARY_PATH
LD_LIBRARY_PATH_dir=/@CATKIN_GLOBAL_LIB_DESTINATION
# bin folder of workspace prepended to PATH
PATH_dir=/@CATKIN_GLOBAL_BIN_DESTINATION
# lib/pkgconfig folder of workspace prepended to PKG_CONFIG_PATH
PKG_CONFIG_PATH_dir=/lib/pkgconfig
# lib/pythonX.Y/..-packages folder of workspace prepended to PYTHONPATH
# the path suffix is calculated in catkin/cmake/python.cmake
PYTHONPATH_dir=/@PYTHON_INSTALL_DIR

# detect if running on Darwin platform
UNAME=`which uname`
UNAME=`$UNAME`
IS_DARWIN=0
if [ "$UNAME" = "Darwin" ]; then
  IS_DARWIN=1
fi

SETUP_UTIL=@SETUP_DIR/_setup_util.py

if [ ! -f "$SETUP_UTIL" ]; then
  echo "Bug: missing util script $SETUP_UTIL"
  return 22
fi

# reset environment variables by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH
# this does not cover modifications performed by environment hooks
if [ $DO_CLEAN -eq 1 ]; then
  export CPATH
  export CPATH="$("$SETUP_UTIL" --remove --name CPATH --value "$CPATH_dir")"
  if [ $IS_DARWIN -eq 0 ]; then
    export LD_LIBRARY_PATH
    export LD_LIBRARY_PATH="$("$SETUP_UTIL" --remove --name LD_LIBRARY_PATH --value "$LD_LIBRARY_PATH_dir")"
  else
    export DYLD_LIBRARY_PATH
    export DYLD_LIBRARY_PATH="$("$SETUP_UTIL" --remove --name DYLD_LIBRARY_PATH --value "$LD_LIBRARY_PATH_dir")"
  fi
  export PATH
  export PATH="$("$SETUP_UTIL" --remove --name PATH --value "$PATH_dir")"
  export PKG_CONFIG_PATH
  export PKG_CONFIG_PATH="$("$SETUP_UTIL" --remove --name PKG_CONFIG_PATH --value "$PKG_CONFIG_PATH_dir")"
  export PYTHONPATH
  export PYTHONPATH="$("$SETUP_UTIL" --remove --name PYTHONPATH --value "$PYTHONPATH_dir")"
  # rollback CMAKE_PREFIX_PATH last since the other rollback calls rely on this
  export CMAKE_PREFIX_PATH
  export CMAKE_PREFIX_PATH="$("$SETUP_UTIL" --remove --name CMAKE_PREFIX_PATH --value "$CMAKE_PREFIX_PATH_dir")"
fi

# prepend CURRENT_WORKSPACE and CURRENT_CMAKE_PREFIX_PATH to CMAKE_PREFIX_PATH
# the complete list must be set so that environment hooks in parent workspace have the full set available
if [ $IS_RECURSIVE -eq 0 ]; then
  export CMAKE_PREFIX_PATH
  export CMAKE_PREFIX_PATH="$("$SETUP_UTIL" --prefix --name CMAKE_PREFIX_PATH --value "@SETUP_DIR:@CMAKE_PREFIX_PATH")$CMAKE_PREFIX_PATH"
fi

# source setup.SHELL from parent workspaces
if [ $IS_RECURSIVE -eq 0 ]; then
  _CATKIN_SETUP_NO_RECURSION=1
  for workspace in $("$SETUP_UTIL" --get-reversed-workspaces --value "@SETUP_DIR"); do
    . "$workspace/setup.$CATKIN_SHELL"
  done
  _CATKIN_SETUP_NO_RECURSION=0
fi

# prepend folders of workspace to environment variables
export CPATH
export CPATH="$("$SETUP_UTIL" --prefix --name CPATH --value "@SETUP_DIR$CPATH_dir")$CPATH"
if [ $IS_DARWIN -eq 0 ]; then
  export LD_LIBRARY_PATH
  export LD_LIBRARY_PATH="$("$SETUP_UTIL" --prefix --name LD_LIBRARY_PATH --value "@SETUP_DIR$LD_LIBRARY_PATH_dir")$LD_LIBRARY_PATH"
else
  export DYLD_LIBRARY_PATH
  export DYLD_LIBRARY_PATH="$("$SETUP_UTIL" --prefix --name DYLD_LIBRARY_PATH --value "@SETUP_DIR$LD_LIBRARY_PATH_dir")$DYLD_LIBRARY_PATH"
fi
export PATH
export PATH="$("$SETUP_UTIL" --prefix --name PATH --value "@SETUP_DIR$PATH_dir")$PATH"
export PKG_CONFIG_PATH
export PKG_CONFIG_PATH="$("$SETUP_UTIL" --prefix --name PKG_CONFIG_PATH --value "@SETUP_DIR$PKG_CONFIG_PATH_dir")$PKG_CONFIG_PATH"
export PYTHONPATH
export PYTHONPATH="$("$SETUP_UTIL" --prefix --name PYTHONPATH --value "@SETUP_DIR$PYTHONPATH_dir")$PYTHONPATH"

# run all environment hooks of this workspace
FIND=`which find`
SORT=`which sort`
ENV_HOOKS_GENERIC=$($FIND "@SETUP_DIR/etc/catkin/profile.d" -maxdepth 1 -name "*.sh" 2>/dev/null | $SORT)
if [ "$CATKIN_SHELL" != "sh" ]; then
  ENV_HOOKS_SPECIFIC=$($FIND "@SETUP_DIR/etc/catkin/profile.d" -maxdepth 1 -name "*.$CATKIN_SHELL" 2>/dev/null | $SORT)
else
  ENV_HOOKS_SPECIFIC=
fi
for envfile in $ENV_HOOKS_GENERIC $ENV_HOOKS_SPECIFIC; do
  . "$envfile"
done

#!/usr/bin/env sh
# generated from catkin/cmake/em/setup.sh.em

# Sets various the enviroment variables and sources additional enviroment hooks.
# The output depends on the CMAKE_PREFIX_PATH and the path of this workspace.
# It tries it's best to undo changes from a previously sourced setup file before.
# Supported command line options:
# --extend: skips the undoing of changes from a previously sourced setup file
# Supported enviroment variables:
# _CATKIN_SETUP_NO_RECURSION: skips it's own recursion (when invoked by another setup file)

# environment at generation time
# CURRENT_CMAKE_PREFIX_PATH="@CMAKE_PREFIX_PATH"
# CURRENT_WORKSPACE="@SETUP_DIR"

# remember type of shell if not already set
if [ -z "$CATKIN_SHELL" ]; then
  CATKIN_SHELL=sh
fi

# decide which commands to perform
DO_CLEAN=1
IS_RECURSIVE=0
if [ "$_CATKIN_SETUP_NO_RECURSION" = "1" ]; then
  DO_CLEAN=0
  IS_RECURSIVE=1
elif [ $# -eq 1 -a "$1" = "--extend" ]; then
  DO_CLEAN=0
fi

# folder of workspace prepended to CMAKE_PREFIX_PATH
CMAKE_PREFIX_PATH_dir=""
# include folder of workspace prepended to CPATH
CPATH_dir=/include
# lib folder of workspace prepended to (DY)LD_LIBRARY_PATH
LD_LIBRARY_PATH_dir=/@CATKIN_GLOBAL_LIB_DESTINATION
# bin folder of workspace prepended to PATH
PATH_dir=/@CATKIN_GLOBAL_BIN_DESTINATION
# lib/pkgconfig folder of workspace prepended to PKG_CONFIG_PATH
PKG_CONFIG_PATH_dir=/lib/pkgconfig
# lib/pythonX.Y/..-packages folder of workspace prepended to PYTHONPATH
# the path suffix is calculated in catkin/cmake/python.cmake
PYTHONPATH_dir=/@PYTHON_INSTALL_DIR

# detect if running on Darwin platform
UNAME=`which uname`
UNAME=`$UNAME`
IS_DARWIN=0
if [ "$UNAME" = "Darwin" ]; then
  IS_DARWIN=1
fi

SETUP_UTIL=@SETUP_DIR/_setup_util.py

if [ ! -f "$SETUP_UTIL" ]; then
  echo "Bug: missing util script $SETUP_UTIL"
  return 22
fi

# reset environment variables by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH
# this does not cover modifications performed by environment hooks
if [ $DO_CLEAN -eq 1 ]; then
  export CPATH="$("$SETUP_UTIL" --remove --name CPATH --value "$CPATH_dir")"
  if [ $IS_DARWIN -eq 0 ]; then
    export LD_LIBRARY_PATH="$("$SETUP_UTIL" --remove --name LD_LIBRARY_PATH --value "$LD_LIBRARY_PATH_dir")"
  else
    export DYLD_LIBRARY_PATH="$("$SETUP_UTIL" --remove --name DYLD_LIBRARY_PATH --value "$LD_LIBRARY_PATH_dir")"
  fi
  export PATH="$("$SETUP_UTIL" --remove --name PATH --value "$PATH_dir")"
  export PKG_CONFIG_PATH="$("$SETUP_UTIL" --remove --name PKG_CONFIG_PATH --value "$PKG_CONFIG_PATH_dir")"
  export PYTHONPATH="$("$SETUP_UTIL" --remove --name PYTHONPATH --value "$PYTHONPATH_dir")"
  # rollback CMAKE_PREFIX_PATH last since the other rollback calls rely on this
  export CMAKE_PREFIX_PATH="$("$SETUP_UTIL" --remove --name CMAKE_PREFIX_PATH --value "$CMAKE_PREFIX_PATH_dir")"
fi

# prepend CURRENT_WORKSPACE and CURRENT_CMAKE_PREFIX_PATH to CMAKE_PREFIX_PATH
# the complete list must be set so that environment hooks in parent workspace have the full set available
if [ $IS_RECURSIVE -eq 0 ]; then
  export CMAKE_PREFIX_PATH="$("$SETUP_UTIL" --prefix --name CMAKE_PREFIX_PATH --value "@SETUP_DIR:@CMAKE_PREFIX_PATH")$CMAKE_PREFIX_PATH"
fi

# source setup.SHELL from parent workspaces
if [ $IS_RECURSIVE -eq 0 ]; then
  _CATKIN_SETUP_NO_RECURSION=1
  for workspace in $("$SETUP_UTIL" --get-reversed-workspaces --value "@SETUP_DIR"); do
    . "$workspace/setup.$CATKIN_SHELL"
  done
  _CATKIN_SETUP_NO_RECURSION=0
fi

# prepend folders of workspace to environment variables
export CPATH="$("$SETUP_UTIL" --prefix --name CPATH --value "@SETUP_DIR$CPATH_dir")$CPATH"
if [ $IS_DARWIN -eq 0 ]; then
  export LD_LIBRARY_PATH="$("$SETUP_UTIL" --prefix --name LD_LIBRARY_PATH --value "@SETUP_DIR$LD_LIBRARY_PATH_dir")$LD_LIBRARY_PATH"
else
  export DYLD_LIBRARY_PATH="$("$SETUP_UTIL" --prefix --name DYLD_LIBRARY_PATH --value "@SETUP_DIR$LD_LIBRARY_PATH_dir")$DYLD_LIBRARY_PATH"
fi
export PATH="$("$SETUP_UTIL" --prefix --name PATH --value "@SETUP_DIR$PATH_dir")$PATH"
export PKG_CONFIG_PATH="$("$SETUP_UTIL" --prefix --name PKG_CONFIG_PATH --value "@SETUP_DIR$PKG_CONFIG_PATH_dir")$PKG_CONFIG_PATH"
export PYTHONPATH="$("$SETUP_UTIL" --prefix --name PYTHONPATH --value "@SETUP_DIR$PYTHONPATH_dir")$PYTHONPATH"

# run all environment hooks of this workspace
FIND=`which find`
SORT=`which sort`
ENV_HOOKS_GENERIC=$($FIND "@SETUP_DIR/etc/catkin/profile.d" -maxdepth 1 -name "*.sh" 2>/dev/null | $SORT)
if [ "$CATKIN_SHELL" != "sh" ]; then
  ENV_HOOKS_SPECIFIC=$($FIND "@SETUP_DIR/etc/catkin/profile.d" -maxdepth 1 -name "*.$CATKIN_SHELL" 2>/dev/null | $SORT)
else
  ENV_HOOKS_SPECIFIC=
fi
for envfile in $ENV_HOOKS_GENERIC $ENV_HOOKS_SPECIFIC; do
  . "$envfile"
done


Minor fix in restoring functions.  Instead of calling eval, use system.eval.

if (typeof(std.persist) === 'undefined')
    throw new Error('Error.  Do not import this file directly.  Only import it from persistService.em');

(function()
 {

     /**
      Each element of this array contains the unique id given to the
      presence object when it was being persisted.  And each element
      correpsonds to a presence that is trying to be restored in the
      world.
      */
     var allPresStillRest = [];

     var mRestoring = false;

     function registerPresenceStillRestoring(presObjID)
     {
         allPresStillRest.push(presObjID);
     }

     /**
      Each restored presence has an onConnected callback.  The
      onConnected callback should be generated from this afterRestored
      function.  All that the callback does is update the nameService
      that we're using with the new presence object and removes this
      presence from the list of presences that we are restoring that
      are still waiting to be connected (in allPresStillRest).

      Second stage of restoration (fixing up pointers to other
      objects) waits until all presences have been connected (polls
      allPresStillRest to see if have zero length).

      @param {int} presObjID, unique name that was given to presence
      before restore.  (ie name that goes into nameService.)

      @param nameService nameService used during restoration.
      */
     function afterRestored(presObjID,nameService)
     {
         var returner = function()
         {
             var toRemove = [];
             for (var s in allPresStillRest)
             {
                 //keeps track of all indices to remove from
                 //allPresStillRest
                 toRemove.push(s);
             }

             //insert the presence into nameService;
             nameService.insertObjectWithName(system.self,presObjID);

             toRemove.reverse();
             for (var s in toRemove)
                 delete allPresStillRest[ toRemove[s]  ];
         };
         return returner;
     }

     function checkIsTriplet(toCheck)
     {
         if ((typeof(toCheck) !== 'object') || (!('length' in toCheck)) || (toCheck.length != 3))
             return false;

         return true;
     }

     function tripletGetIndexName(triplet)
     {
         if (!checkIsTriplet(triplet))
         {
             system.prettyprint(triplet);
             throw new Error('Error in tripletGetIndexName.  Requires triplet to be passed in');
         }

         return triplet[0];
     }

     function tripletGetValue(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetValue.  Requires triplet to be passed in');

         var type = tripletGetType(triplet);
         if (type == 'number')
             return parseFloat(triplet[1]);
         if (type == 'string')
             return triplet[1];
         if (type == 'boolean')
             return (triplet[1] == 'true');

         return triplet[1];
     }

     function tripletGetType(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetType.  Requires triplet to be passed in');
         return triplet[2];
     }

    /**
     @param {array} triplet Array containing three values.  Indices of
     array follow:
       0 : index
       1 : value to set to/object pointer/'null'
       2 : type record

     @return {boolean} Returns true if the value in this object is a
     value type
     */
    function tripletIsValueType (triplet)
    {
        if (! checkIsTriplet(triplet))
            throw new Error('Error in checkTripletIsValueType.  Requires triplet to be passed in');

        var tripType = tripletGetType(triplet);

        if ((tripType != 'object') && (tripType != 'function') &&
            (tripType!=std.persist.SYSTEM_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.VISIBLE_TYPE_STRING) &&
            (tripType!=std.persist.TIMER_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_ENTRY_TYPE_STRING) &&
            (tripType!=std.persist.FUNCTION_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.BASIC_OBJECT_TYPE_STRING))
            return true;

        return false;
    };

     /**
      @param {int} ptrId The unique name of the object that should be
      referenced by calling localCopyToPoint[index]
      @param index of the local object to perform fixup on.
      @param {object} localCopyToPoint localCopyToPoint[index] should be the new
      object pointer
      @param {array} allToFix an array.  Push the above three arguments onto that
      array (as an array).

      After rest of deserialization occurs, final step is to call
      performFinalFixups with this array as an argument.
      performPtrFinalFixups traverses array, and points
      localCopyToPoint[index] to the objects described by objPtrToFix.
      */
     function registerFixupObjectPointer(ptrId, index, localCopyToPoint, allToFix)
     {
         allToFix.push([ptrId, index,localCopyToPoint]);
     };

     /**
      locaCopyToPoint[index] should end up pointing to the object represented
      by the lookup into nameService of ptrId

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function fixSinglePtr (ptrId, index,localCopyToPoint,nameService)
     {
         if ((typeof(localCopyToPoint) != 'object') || (localCopyToPoint== null))
             throw new Error('Error in fixSinglePtr.  Should not have received non-obect or null ptr record.');

         var fixedObj = nameService.lookupObject(ptrId);
         if (fixedObj == nameService.DNE)
             throw new Error('Error in fixSinglePtr.  Have no record of object that you are trying to point to.');

         localCopyToPoint[index] = fixedObj;
     };

     /**
      @param {array} allToFix array.  Each element has three values. [0]
      ptrId, [1] index, [2] localCopyToPoint.

      Runs through array generated by calls to registerFixupObjectPointer,
      and individually performs fixups for each one.

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function performPtrFinalFixups(allToFix,nameService)
     {
         for (var s in allToFix)
             fixSinglePtr(allToFix[s][0],allToFix[s][1],allToFix[s][2],nameService);
     };

     /**
      fixReferences opens the keyName table, and returns an object whose
      subgraph is rooted at the object with id ptrId. Note: all objects the
      root object references may not be linked to correctly.  Need to call
      performPtrFinalFixups on ptrsToFix after running this function.

      @param {keyName}, Passed to the backend.  This is the entry to
      access.

      @param {int}, ptrId, Each object has a unique id.  ptrId is the
      identifier for the object that we should be fixing references for (in
      the keyName table).

      @param {array} ptrsToFix is an array that tracks all objects that we
      will need to fixup the references too.  See performFixupPtr function.

      @param nameService can be used to lookup the ids for objects that
      we've already re-read.

      @param cb callback to invoke when complete. Passed success flag (boolean)
      and, if successful, an object whose subgraph is rooted at the object with id ptrId.

      */
     function fixReferences(keyName, ptrId, ptrsToFix, nameService, cb)
     {
         readObject(
             keyName,ptrId,
             std.core.bind(finishFixReferences, undefined, keyName, ptrId, ptrsToFix, nameService, cb) // + success, object
         );
     }

     function finishFixReferences(keyName, ptrId, ptrsToFix, nameService, cb, success, unfixedObj) {
         if (!success)
         {
             cb(false);
             return;
         }

         var id = unfixedObj['mID'];
         if (id != ptrId) //throw 'Error: ptrId and object id must be identical';
             cb(false);

         if (nameService.lookupObject(ptrId) != nameService.DNE) // throw "Error.  Called fixReferences on an object I've already visited";
             cb(false);

         var type = unfixedObj['type'];
         if (type == std.persist.FUNCTION_OBJECT_TYPE_STRING)
             restoreFunction(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.PRESENCE_OBJECT_TYPE_STRING)
             restorePresence(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.BASIC_OBJECT_TYPE_STRING)
             restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VEC_TYPE_STRING)
             restoreVec3Object(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.QUAT_TYPE_STRING)
             restoreQuatObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VISIBLE_TYPE_STRING)
             restoreVisibleObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else
             throw new Error('Error in fixReferences.  Do not have any other types in the system to restore from.');
     }

     /**
      @see fixReferences for arguments
      Returns a new visible object.
      */
     function restoreVisibleObject(keyName, visRecord,ptrId,ptrsToFix,nameService,cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,visRecord,ptrId,pToFix,nameService,
             std.core.bind(finishRestoreVisibleObject, undefined, keyname, visRecord, ptrId, pToFix, nameService, cb)
         );
     };
     function finishRestoreVisibleObject(keyName, visRecord,ptrId,pToFix,nameService,cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         var returner = system.createVisible(
             toRestoreFrom.sporef,
             toRestoreFrom.sporefFrom,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.pos,
             toRestoreFrom.scale,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.solidAngleQuery
         );

         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new vec3.
      */
     function restoreVec3Object(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         //lkjs: may want some check here.
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var returner = new util.Vec3(x,y,z);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new quaternion.
      */
     function restoreQuatObject(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var w = getValueField(unfixedObj,'w');

         var returner = new util.Quaternion(x,y,z,w);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @param unfixedObj Is an object that is read in directly from the
      backend.  It has three types of fields: 1) triplets, 2) mID (obj
      id), 3) type (obj type).

      @param Field name that we're looking for in one of the triplets.

      @return returns the value of the triplet associated with
      filedName

      @throw Throws an error if fieldName does not exist as a field in
      any of unfixedObj's triplets.

      This function runs through all the triplets in unfixedObj, and
      returns the value of the triplet that matches the field.
      */
     function getValueField(unfixedObj,fieldName)
     {
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var propValTypeTriple = unfixedObj[s];

             var tripFieldName = tripletGetIndexName(unfixedObj[s]);
             if (tripFieldName == fieldName)
                 return tripletGetValue(propValTypeTriple);
         }
         throw new Error('Error in getValueField.  Have no field named: '+ fieldName);
     }

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedPres has all the data that you would get from a call to
      getAllData on a presence.

      */
     function restorePresence(keyName,unfixedPres,ptrId,ptrsToFix,nameService, cb)
     {

         var pToFix = [];
         restoreBasicObject(
             keyName,unfixedPres,ptrId,pToFix,nameService,
             std.core.bind(finishRestorePresence, undefined, keyName, unfixedPres, ptrId, pToFix, nameService, cb) // + success, obj
         );
     }
     function finishRestorePresence(keyName,unfixedPres,ptrId,pToFix,nameService, cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         for (var s in system.presences)
         {
             if (system.presences[s].toString() == toRestoreFrom.sporef)
             {
                 cb(true,system.presences[s]);
                 return;
             }
         }

         system.print('\n\nDEBUG: Got into presence restore.\n');
         var onConnectCB = afterRestored(ptrId,nameService);
         system.restorePresence(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.mesh,
             toRestoreFrom.scale,
             toRestoreFrom.isCleared,
             toRestoreFrom.contextId,
             toRestoreFrom.isConnected,
             onConnectCB,
             toRestoreFrom.isSuspended,
             toRestoreFrom.suspendedVelocity,
             toRestoreFrom.suspendedOrientationVelocity,
             toRestoreFrom.solidAngleQuery
         );

         //tells the system that we have begun trying to restore this
         //presence, and not to continue with later stages of
         //restoration (fixing looped object pointers) until the
         //system has connected this presence.
         registerPresenceStillRestoring(ptrId);
         cb(true, null); // Not sure about this being null....
     };

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedFunc right now just has one field: 'funcField' with the
      text of the function that we're creating.
      */
     function restoreFunction(keyName,unfixedFunc,ptrId,ptrsToFix,nameService, cb)
     {
         //FIXME: lkjs;  GROSS!
         var funcTriple = unfixedFunc[0];

         var index = tripletGetIndexName(funcTriple);
         if (index != 'funcField')
             throw new Error('Error restoring function.  Restoring object does not have funcField.');

         var funcAsString = tripletGetValue(funcTriple);

         var returner = null;
         var success = true;
         try
         {
             returner = system.eval(funcAsString);
         }
         catch (excep)
         {
             system.print('ERROR: Error in restoreFunction.  Trying to restore a function with faulty syntax.');
             returner = function(){};
             success = false;
         }
         nameService.insertObjectWithName(returner,ptrId);
         cb(success, returner);
     }

     /**
      For keyName, ptrId,ptrsToFix, and nameService, @see fixReferences.

      @param unfixedObj is the object that we get back from reading
      from reading from backend.  It is a basic object (not a
      function, presence, system obj, etc.).  This function runs
      through all of its fields and registers them to be fixed up.

      */
     function restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb)
     {
         var returner = { };
         nameService.insertObjectWithName(returner,ptrId);
         //run through all the fields in unfixedObj.  If the field is a
         //value type, point returner to the new field right away.  If the
         //field is an object type and we have a copy of that object, then
         //point returner to that new object right away.  If the field is
         //an object type and we do not have a copy of that object, then
         //register returner to be fixed-up with the new pointer later.

         var unfinished_fields = 0;
         var did_callback = false;
         var finish_field_cb = function(success, val) {
             if (!success && !did_callback) {
                 did_callback = true;
                 cb(false);
                 return;
             }
             unfinished_fields -= 1;
             if (unfinished_fields == 0) {
                 did_callback = true;
                 cb(true, returner);
             }
         };
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var index = tripletGetIndexName(unfixedObj[s]);

             //sets to value type
             if (tripletIsValueType(unfixedObj[s]))
                 returner[index] = tripletGetValue(unfixedObj[s]);
             else
             {
                 if ((tripletGetType(unfixedObj[s])) == 'null')
                     returner[index] = null;
                 else
                 {
                     var ptrIdInner  = tripletGetValue(unfixedObj[s]);
                     var ptrObj = nameService.lookupObject(ptrIdInner);
                     if (ptrObj != nameService.DNE)
                     {
                         //already have the object.  just link to it.
                         returner[index] = ptrObj;
                     }
                     else
                     {
                         //will have to register this pointer to be fixed up
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);
                         unfinished_fields += 1;
                         fixReferences(keyName,ptrIdInner,ptrsToFix,nameService, std.core.bind(finish_field_cb, undefined));
                     }
                 }
             }
         }

         // Special case for empty objects
         if (unfinished_fields == 0)
             cb(true, returner);
     };

     /**
      Returns true if we're in the middle of a restore operation.
      Otherwise, returns false.
      */
     std.persist.inRestore = function()
     {
         return mRestoring;
     };

     /**
      @see std.persist.restoreFromAndGetNamesAsync
      */
     std.persist.restoreFromAsync = function (keyName,cb)
     {
         return std.persist.restoreFromAndGetNamesAsync(keyName,0,cb);
     };

     /**
      This function should be used if the object graph that you are
      trying to restore has presences in it, and you need to wait for
      the system to actually connect these presences.

      @param cb Takes in three parameters, first is restored obj graph
      (if pres failure, all presences become null objs.), second
      boolean for success (ie true if eveything restored
      appropriately, and space allowed new presences to reconnect,
      false otherwise), the third is the nameService that we used.
      */
     std.persist.restoreFromAndGetNamesAsync = function (keyName,id,cb)
     {
         if (std.persist.inRestore())
             throw new Error('Error, cannot request additional restores when in middle of current restore.  Check back later.');
         mRestoring = true;

         var nameService = new std.persist.NameService();
         var ptrsToFix = [];
         fixReferences(
             keyName, id,ptrsToFix,nameService,
             std.core.bind(finishRestoreFromAndGetNamesAsync, undefined, keyName, id, cb, ptrsToFix, nameService) // + success, obj
         );
     };

     var finishRestoreFromAndGetNamesAsync = function (keyName,id,cb, ptrsToFix, nameService, success, returner) {
         if (!success) {
             mRestoring = false;
             cb(false);
             return;
         }
         if (allPresStillRest.length == 0)
         {
             performPtrFinalFixups(ptrsToFix,nameService);
             mRestoring = false;
             cb(true,returner,nameService);
             return;
         }

         //if we haven't restored all presences after five seconds, then we're just not going to.
         var restoreCheckback = function()
         {
             // means that we were trying to restore a presence;
             // should just return that presence instead;
             if (returner == null)
             {
                 var tRet = nameService.lookupObject(id);
                 if (tRet != nameService.DNE)
                 {
                     returner = tRet;
                     system.changeSelf(returner.toString());
                 }
             }
             performPtrFinalFixups(ptrsToFix,nameService);
             mRestoring = false;
             allPresStillRest = [];
             if (allPresStillRest.length == 0)
                 cb(true, returner, nameService);
             else
                 cb(false, returner, nameService);
         };

         system.timeout(5,restoreCheckback);
     };

 })();

if (typeof(std.persist) === 'undefined')
    throw new Error('Error.  Do not import this file directly.  Only import it from persistService.em');

(function()
 {

     /**
      Each element of this array contains the unique id given to the
      presence object when it was being persisted.  And each element
      correpsonds to a presence that is trying to be restored in the
      world.
      */
     var allPresStillRest = [];

     var mRestoring = false;

     function registerPresenceStillRestoring(presObjID)
     {
         allPresStillRest.push(presObjID);
     }

     /**
      Each restored presence has an onConnected callback.  The
      onConnected callback should be generated from this afterRestored
      function.  All that the callback does is update the nameService
      that we're using with the new presence object and removes this
      presence from the list of presences that we are restoring that
      are still waiting to be connected (in allPresStillRest).

      Second stage of restoration (fixing up pointers to other
      objects) waits until all presences have been connected (polls
      allPresStillRest to see if have zero length).

      @param {int} presObjID, unique name that was given to presence
      before restore.  (ie name that goes into nameService.)

      @param nameService nameService used during restoration.
      */
     function afterRestored(presObjID,nameService)
     {
         var returner = function()
         {
             var toRemove = [];
             for (var s in allPresStillRest)
             {
                 //keeps track of all indices to remove from
                 //allPresStillRest
                 toRemove.push(s);
             }

             //insert the presence into nameService;
             nameService.insertObjectWithName(system.self,presObjID);

             toRemove.reverse();
             for (var s in toRemove)
                 delete allPresStillRest[ toRemove[s]  ];
         };
         return returner;
     }

     function checkIsTriplet(toCheck)
     {
         if ((typeof(toCheck) !== 'object') || (!('length' in toCheck)) || (toCheck.length != 3))
             return false;

         return true;
     }

     function tripletGetIndexName(triplet)
     {
         if (!checkIsTriplet(triplet))
         {
             system.prettyprint(triplet);
             throw new Error('Error in tripletGetIndexName.  Requires triplet to be passed in');
         }

         return triplet[0];
     }

     function tripletGetValue(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetValue.  Requires triplet to be passed in');

         var type = tripletGetType(triplet);
         if (type == 'number')
             return parseFloat(triplet[1]);
         if (type == 'string')
             return triplet[1];
         if (type == 'boolean')
             return (triplet[1] == 'true');

         return triplet[1];
     }

     function tripletGetType(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetType.  Requires triplet to be passed in');
         return triplet[2];
     }

    /**
     @param {array} triplet Array containing three values.  Indices of
     array follow:
       0 : index
       1 : value to set to/object pointer/'null'
       2 : type record

     @return {boolean} Returns true if the value in this object is a
     value type
     */
    function tripletIsValueType (triplet)
    {
        if (! checkIsTriplet(triplet))
            throw new Error('Error in checkTripletIsValueType.  Requires triplet to be passed in');

        var tripType = tripletGetType(triplet);

        if ((tripType != 'object') && (tripType != 'function') &&
            (tripType!=std.persist.SYSTEM_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.VISIBLE_TYPE_STRING) &&
            (tripType!=std.persist.TIMER_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_ENTRY_TYPE_STRING) &&
            (tripType!=std.persist.FUNCTION_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.BASIC_OBJECT_TYPE_STRING))
            return true;

        return false;
    };

     /**
      @param {int} ptrId The unique name of the object that should be
      referenced by calling localCopyToPoint[index]
      @param index of the local object to perform fixup on.
      @param {object} localCopyToPoint localCopyToPoint[index] should be the new
      object pointer
      @param {array} allToFix an array.  Push the above three arguments onto that
      array (as an array).

      After rest of deserialization occurs, final step is to call
      performFinalFixups with this array as an argument.
      performPtrFinalFixups traverses array, and points
      localCopyToPoint[index] to the objects described by objPtrToFix.
      */
     function registerFixupObjectPointer(ptrId, index, localCopyToPoint, allToFix)
     {
         allToFix.push([ptrId, index,localCopyToPoint]);
     };

     /**
      locaCopyToPoint[index] should end up pointing to the object represented
      by the lookup into nameService of ptrId

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function fixSinglePtr (ptrId, index,localCopyToPoint,nameService)
     {
         if ((typeof(localCopyToPoint) != 'object') || (localCopyToPoint== null))
             throw new Error('Error in fixSinglePtr.  Should not have received non-obect or null ptr record.');

         var fixedObj = nameService.lookupObject(ptrId);
         if (fixedObj == nameService.DNE)
             throw new Error('Error in fixSinglePtr.  Have no record of object that you are trying to point to.');

         localCopyToPoint[index] = fixedObj;
     };

     /**
      @param {array} allToFix array.  Each element has three values. [0]
      ptrId, [1] index, [2] localCopyToPoint.

      Runs through array generated by calls to registerFixupObjectPointer,
      and individually performs fixups for each one.

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function performPtrFinalFixups(allToFix,nameService)
     {
         for (var s in allToFix)
             fixSinglePtr(allToFix[s][0],allToFix[s][1],allToFix[s][2],nameService);
     };

     /**
      fixReferences opens the keyName table, and returns an object whose
      subgraph is rooted at the object with id ptrId. Note: all objects the
      root object references may not be linked to correctly.  Need to call
      performPtrFinalFixups on ptrsToFix after running this function.

      @param {keyName}, Passed to the backend.  This is the entry to
      access.

      @param {int}, ptrId, Each object has a unique id.  ptrId is the
      identifier for the object that we should be fixing references for (in
      the keyName table).

      @param {array} ptrsToFix is an array that tracks all objects that we
      will need to fixup the references too.  See performFixupPtr function.

      @param nameService can be used to lookup the ids for objects that
      we've already re-read.

      @param cb callback to invoke when complete. Passed success flag (boolean)
      and, if successful, an object whose subgraph is rooted at the object with id ptrId.

      */
     function fixReferences(keyName, ptrId, ptrsToFix, nameService, cb)
     {
         readObject(
             keyName,ptrId,
             std.core.bind(finishFixReferences, undefined, keyName, ptrId, ptrsToFix, nameService, cb) // + success, object
         );
     }

     function finishFixReferences(keyName, ptrId, ptrsToFix, nameService, cb, success, unfixedObj) {
         if (!success)
         {
             cb(false);
             return;
         }

         var id = unfixedObj['mID'];
         if (id != ptrId) //throw 'Error: ptrId and object id must be identical';
             cb(false);

         if (nameService.lookupObject(ptrId) != nameService.DNE) // throw "Error.  Called fixReferences on an object I've already visited";
             cb(false);

         var type = unfixedObj['type'];
         if (type == std.persist.FUNCTION_OBJECT_TYPE_STRING)
             restoreFunction(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.PRESENCE_OBJECT_TYPE_STRING)
             restorePresence(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.BASIC_OBJECT_TYPE_STRING)
             restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VEC_TYPE_STRING)
             restoreVec3Object(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.QUAT_TYPE_STRING)
             restoreQuatObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VISIBLE_TYPE_STRING)
             restoreVisibleObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else
             throw new Error('Error in fixReferences.  Do not have any other types in the system to restore from.');
     }

     /**
      @see fixReferences for arguments
      Returns a new visible object.
      */
     function restoreVisibleObject(keyName, visRecord,ptrId,ptrsToFix,nameService,cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,visRecord,ptrId,pToFix,nameService,
             std.core.bind(finishRestoreVisibleObject, undefined, keyname, visRecord, ptrId, pToFix, nameService, cb)
         );
     };
     function finishRestoreVisibleObject(keyName, visRecord,ptrId,pToFix,nameService,cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         var returner = system.createVisible(
             toRestoreFrom.sporef,
             toRestoreFrom.sporefFrom,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.pos,
             toRestoreFrom.scale,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.solidAngleQuery
         );

         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new vec3.
      */
     function restoreVec3Object(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         //lkjs: may want some check here.
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var returner = new util.Vec3(x,y,z);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new quaternion.
      */
     function restoreQuatObject(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var w = getValueField(unfixedObj,'w');

         var returner = new util.Quaternion(x,y,z,w);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @param unfixedObj Is an object that is read in directly from the
      backend.  It has three types of fields: 1) triplets, 2) mID (obj
      id), 3) type (obj type).

      @param Field name that we're looking for in one of the triplets.

      @return returns the value of the triplet associated with
      filedName

      @throw Throws an error if fieldName does not exist as a field in
      any of unfixedObj's triplets.

      This function runs through all the triplets in unfixedObj, and
      returns the value of the triplet that matches the field.
      */
     function getValueField(unfixedObj,fieldName)
     {
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var propValTypeTriple = unfixedObj[s];

             var tripFieldName = tripletGetIndexName(unfixedObj[s]);
             if (tripFieldName == fieldName)
                 return tripletGetValue(propValTypeTriple);
         }
         throw new Error('Error in getValueField.  Have no field named: '+ fieldName);
     }

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedPres has all the data that you would get from a call to
      getAllData on a presence.

      */
     function restorePresence(keyName,unfixedPres,ptrId,ptrsToFix,nameService, cb)
     {

         var pToFix = [];
         restoreBasicObject(
             keyName,unfixedPres,ptrId,pToFix,nameService,
             std.core.bind(finishRestorePresence, undefined, keyName, unfixedPres, ptrId, pToFix, nameService, cb) // + success, obj
         );
     }
     function finishRestorePresence(keyName,unfixedPres,ptrId,pToFix,nameService, cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         for (var s in system.presences)
         {
             if (system.presences[s].toString() == toRestoreFrom.sporef)
             {
                 cb(true,system.presences[s]);
                 return;
             }
         }

         system.print('\n\nDEBUG: Got into presence restore.\n');
         var onConnectCB = afterRestored(ptrId,nameService);
         system.restorePresence(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.mesh,
             toRestoreFrom.scale,
             toRestoreFrom.isCleared,
             toRestoreFrom.contextId,
             toRestoreFrom.isConnected,
             onConnectCB,
             toRestoreFrom.isSuspended,
             toRestoreFrom.suspendedVelocity,
             toRestoreFrom.suspendedOrientationVelocity,
             toRestoreFrom.solidAngleQuery
         );

         //tells the system that we have begun trying to restore this
         //presence, and not to continue with later stages of
         //restoration (fixing looped object pointers) until the
         //system has connected this presence.
         registerPresenceStillRestoring(ptrId);
         cb(true, null); // Not sure about this being null....
     };

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedFunc right now just has one field: 'funcField' with the
      text of the function that we're creating.
      */
     function restoreFunction(keyName,unfixedFunc,ptrId,ptrsToFix,nameService, cb)
     {
         //FIXME: lkjs;  GROSS!
         var funcTriple = unfixedFunc[0];

         var index = tripletGetIndexName(funcTriple);
         if (index != 'funcField')
             throw new Error('Error restoring function.  Restoring object does not have funcField.');

         var funcAsString = tripletGetValue(funcTriple);

         var returner = null;
         var success = true;
         try
         {
             returner = eval(funcAsString);
         }
         catch (excep)
         {
             system.print('ERROR: Error in restoreFunction.  Trying to restore a function with faulty syntax.');
             returner = function(){};
             success = false;
         }
         nameService.insertObjectWithName(returner,ptrId);
         cb(success, returner);
     }

     /**
      For keyName, ptrId,ptrsToFix, and nameService, @see fixReferences.

      @param unfixedObj is the object that we get back from reading
      from reading from backend.  It is a basic object (not a
      function, presence, system obj, etc.).  This function runs
      through all of its fields and registers them to be fixed up.

      */
     function restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb)
     {
         var returner = { };
         nameService.insertObjectWithName(returner,ptrId);
         //run through all the fields in unfixedObj.  If the field is a
         //value type, point returner to the new field right away.  If the
         //field is an object type and we have a copy of that object, then
         //point returner to that new object right away.  If the field is
         //an object type and we do not have a copy of that object, then
         //register returner to be fixed-up with the new pointer later.

         var unfinished_fields = 0;
         var did_callback = false;
         var finish_field_cb = function(success, val) {
             if (!success && !did_callback) {
                 did_callback = true;
                 cb(false);
                 return;
             }
             unfinished_fields -= 1;
             if (unfinished_fields == 0) {
                 did_callback = true;
                 cb(true, returner);
             }
         };
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var index = tripletGetIndexName(unfixedObj[s]);

             //sets to value type
             if (tripletIsValueType(unfixedObj[s]))
                 returner[index] = tripletGetValue(unfixedObj[s]);
             else
             {
                 if ((tripletGetType(unfixedObj[s])) == 'null')
                     returner[index] = null;
                 else
                 {
                     var ptrIdInner  = tripletGetValue(unfixedObj[s]);
                     var ptrObj = nameService.lookupObject(ptrIdInner);
                     if (ptrObj != nameService.DNE)
                     {
                         //already have the object.  just link to it.
                         returner[index] = ptrObj;
                     }
                     else
                     {
                         //will have to register this pointer to be fixed up
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);
                         unfinished_fields += 1;
                         fixReferences(keyName,ptrIdInner,ptrsToFix,nameService, std.core.bind(finish_field_cb, undefined));
                     }
                 }
             }
         }

         // Special case for empty objects
         if (unfinished_fields == 0)
             cb(true, returner);
     };

     /**
      Returns true if we're in the middle of a restore operation.
      Otherwise, returns false.
      */
     std.persist.inRestore = function()
     {
         return mRestoring;
     };

     /**
      @see std.persist.restoreFromAndGetNamesAsync
      */
     std.persist.restoreFromAsync = function (keyName,cb)
     {
         return std.persist.restoreFromAndGetNamesAsync(keyName,0,cb);
     };

     /**
      This function should be used if the object graph that you are
      trying to restore has presences in it, and you need to wait for
      the system to actually connect these presences.

      @param cb Takes in three parameters, first is restored obj graph
      (if pres failure, all presences become null objs.), second
      boolean for success (ie true if eveything restored
      appropriately, and space allowed new presences to reconnect,
      false otherwise), the third is the nameService that we used.
      */
     std.persist.restoreFromAndGetNamesAsync = function (keyName,id,cb)
     {
         if (std.persist.inRestore())
             throw new Error('Error, cannot request additional restores when in middle of current restore.  Check back later.');
         mRestoring = true;

         var nameService = new std.persist.NameService();
         var ptrsToFix = [];
         fixReferences(
             keyName, id,ptrsToFix,nameService,
             std.core.bind(finishRestoreFromAndGetNamesAsync, undefined, keyName, id, cb, ptrsToFix, nameService) // + success, obj
         );
     };

     var finishRestoreFromAndGetNamesAsync = function (keyName,id,cb, ptrsToFix, nameService, success, returner) {
         if (!success) {
             mRestoring = false;
             cb(false);
             return;
         }
         if (allPresStillRest.length == 0)
         {
             performPtrFinalFixups(ptrsToFix,nameService);
             mRestoring = false;
             cb(true,returner,nameService);
             return;
         }

         //if we haven't restored all presences after five seconds, then we're just not going to.
         var restoreCheckback = function()
         {
             // means that we were trying to restore a presence;
             // should just return that presence instead;
             if (returner == null)
             {
                 var tRet = nameService.lookupObject(id);
                 if (tRet != nameService.DNE)
                 {
                     returner = tRet;
                     system.changeSelf(returner.toString());
                 }
             }
             performPtrFinalFixups(ptrsToFix,nameService);
             mRestoring = false;
             allPresStillRest = [];
             if (allPresStillRest.length == 0)
                 cb(true, returner, nameService);
             else
                 cb(false, returner, nameService);
         };

         system.timeout(5,restoreCheckback);
     };

 })();