WorkspaceObjectVector WorkspaceExtensibleGroup::getTargets() const {
  WorkspaceObjectVector result;
  if (empty()) { return result; }
  for (unsigned fieldIndex = 0, n = numFields(); fieldIndex < n; ++fieldIndex) {
    OptionalWorkspaceObject owo = getTarget(fieldIndex);
    if (owo) { result.push_back(*owo); }
  }
  return result;
}
 std::vector<WorkspaceObject> WorkspaceObjectOrder_Impl::getObjects(
     const std::vector<Handle>& handles) const 
 {
   WorkspaceObjectVector objects;
   // loop through handles and try to find objects
   for (const Handle& handle : handles) {
     OS_ASSERT(m_objectGetter);
     OptionalWorkspaceObject object = m_objectGetter(handle);
     if (object) { objects.push_back(*object); }
   }
   return objects;
 }
TEST_F(IdfFixture,WorkspaceObjectOrder_ByIddObjectType) {
  Workspace workspace(IdfFixture::epIdfFile,openstudio::StrictnessLevel::Draft);
  WorkspaceObjectVector objectsInOriginalOrder = workspace.objects(true);

  WorkspaceObjectOrder wsOrder = workspace.order();
  IddObjectTypeVector orderByType;
  IntSet enumValues = IddObjectType::getValues();
  for (int val : enumValues) {
    orderByType.push_back(IddObjectType(val));
  }
  wsOrder.setIddOrder(orderByType);
  WorkspaceObjectVector objectsInNewOrder = workspace.objects(true);
  EXPECT_EQ(objectsInOriginalOrder.size(),objectsInNewOrder.size());
  EXPECT_FALSE(objectsInOriginalOrder == objectsInNewOrder);

  // expect Materials before Constructions
  OptionalWorkspaceObject oMaterial;
  OptionalWorkspaceObject oConstruction;
  for (const WorkspaceObject& object : objectsInNewOrder) {
    if (!oMaterial && (object.iddObject().type() == IddObjectType::Material)) {
      oMaterial = object;
      EXPECT_FALSE(oConstruction);
      OptionalUnsigned oIndex = wsOrder.indexInOrder(object.handle());
      EXPECT_FALSE(oIndex);
    }
    if (!oConstruction && (object.iddObject().type() == IddObjectType::Construction)) {
      oConstruction = object;
    }
    if (oMaterial && oConstruction) { break; }
  }

  // change to Constructions before Materials
  wsOrder.move(IddObjectType::Construction,IddObjectType::Material);
  objectsInNewOrder = workspace.objects(true);
  oMaterial = boost::none;
  oConstruction = boost::none;
  for (const WorkspaceObject& object : objectsInNewOrder) {
    if (!oMaterial && (object.iddObject().type() == IddObjectType::Material)) {
      oMaterial = object;
    }
    if (!oConstruction && (object.iddObject().type() == IddObjectType::Construction)) {
      oConstruction = object;
      EXPECT_FALSE(oMaterial);
    }
    if (oMaterial && oConstruction) { break; }
  }

}
TEST_F(IdfFixture, WorkspaceObject_Filter_Sources)
{
  Workspace ws;
  OptionalWorkspaceObject node = ws.addObject(IdfObject(IddObjectType::OS_Node));
  OptionalWorkspaceObject node2 = ws.addObject(IdfObject(IddObjectType::OS_Node));
  OptionalWorkspaceObject node3 = ws.addObject(IdfObject(IddObjectType::OS_Node));
  OptionalWorkspaceObject spm = ws.addObject(IdfObject(IddObjectType::OS_SetpointManager_MixedAir));

  EXPECT_TRUE(spm->setPointer(OS_SetpointManager_MixedAirFields::SetpointNodeorNodeListName, node->handle()));
  EXPECT_TRUE(spm->setPointer(OS_SetpointManager_MixedAirFields::FanInletNodeName,node->handle()));
  EXPECT_TRUE(spm->setPointer(OS_SetpointManager_MixedAirFields::FanOutletNodeName,node2->handle()));
  EXPECT_TRUE(spm->setPointer(OS_SetpointManager_MixedAirFields::ReferenceSetpointNodeName,node3->handle()));

  WorkspaceObjectVector sourcesVector = node->sources();
  EXPECT_EQ(1, sourcesVector.size());
  sourcesVector = node->getSources(IddObjectType::OS_SetpointManager_MixedAir);
  EXPECT_EQ(1, sourcesVector.size());
}
TEST_F(IdfFixture,WorkspaceObject_ClearGroups) {
  // always works in None or Draft strictness
  Workspace ws(epIdfFile);
  EXPECT_TRUE(ws.strictnessLevel() == StrictnessLevel::None);
  WorkspaceObjectVector surfaces = ws.getObjectsByType(IddObjectType::BuildingSurface_Detailed);
  ASSERT_TRUE(surfaces.size() > 0);
  unsigned n = surfaces[0].numFields();
  surfaces[0].clearExtensibleGroups();
  EXPECT_TRUE(surfaces[0].numFields() < n);

  // doesn't allow drops below minFields() in Final strictness
  ws = Workspace(epIdfFile);
  ws.setStrictnessLevel(StrictnessLevel::Final);
  surfaces = ws.getObjectsByType(IddObjectType::BuildingSurface_Detailed);
  ASSERT_TRUE(surfaces.size() > 0);
  n = surfaces[0].numFields();
  surfaces[0].clearExtensibleGroups();
  EXPECT_EQ(n,surfaces[0].numFields());
}
// Test in context of Workspace, since order needs objectGetter.
TEST_F(IdfFixture,WorkspaceObjectOrder) {
  Workspace workspace(IdfFixture::epIdfFile,openstudio::StrictnessLevel::Draft);

  WorkspaceObjectOrder wsOrder = workspace.order();

  EXPECT_TRUE(wsOrder.inOrder(workspace.objects()[0].handle()));

  // save current order
  OptionalHandleVector workspaceOrder = wsOrder.directOrder();
  ASSERT_TRUE(workspaceOrder);

  // order by enum
  wsOrder.setOrderByIddEnum();
  WorkspaceObjectVector objects = workspace.objects(true);
  for (WorkspaceObjectVector::const_iterator it = objects.begin(), itEnd = objects.end() - 1;
       it != itEnd; ++ it) {
    WorkspaceObjectVector::const_iterator nxt = it; ++nxt;
    EXPECT_TRUE(it->iddObject().type() <= nxt->iddObject().type());
  }

  // restore order
  wsOrder.setDirectOrder(*workspaceOrder);
  HandleVector handles = workspace.handles(true);
  // handles does not include version object, while direct order does
  HandleVector tempOrder = *workspaceOrder;
  HandleVector::iterator it = std::find(tempOrder.begin(),tempOrder.end(),workspace.versionObject()->handle());
  tempOrder.erase(it);
  EXPECT_TRUE(tempOrder == handles);

  // move objects directly
  wsOrder.insert(handles[32],handles[12]);
  HandleVector newOrder = workspace.handles(true);
  EXPECT_EQ(handles.size(),newOrder.size());
  EXPECT_TRUE(handles[32] == newOrder[12]);
  EXPECT_TRUE(handles[12] == newOrder[13]);

  wsOrder.swap(handles[80],handles[100]);
  newOrder = workspace.handles(true);
  EXPECT_EQ(handles.size(),newOrder.size());
  EXPECT_TRUE(handles[80] == newOrder[100]);
  EXPECT_TRUE(handles[100] == newOrder[80]);
}
TEST_F(IdfFixture, WorkspaceObject_Building) {

  Workspace workspace(epIdfFile, StrictnessLevel::Draft);
  WorkspaceObjectVector buildings = workspace.getObjectsByType(IddObjectType::Building);
  ASSERT_EQ(static_cast<size_t>(1), buildings.size());
  WorkspaceObject building = buildings[0];

  /*
  Building,
    Building,                !- Name
    30.,                     !- North Axis {deg}
    City,                    !- Terrain
    0.04,                    !- Loads Convergence Tolerance Value
    0.4,                     !- Temperature Convergence Tolerance Value {deltaC}
    FullExterior,            !- Solar Distribution
    25;                      !- Maximum Number of Warmup Days
  */
  
  OptionalString buildingName = building.getString(BuildingFields::Name);
  ASSERT_TRUE(buildingName);
  EXPECT_EQ("Building", *buildingName);

  OptionalString northAxisString = building.getString(BuildingFields::NorthAxis);
  ASSERT_TRUE(northAxisString);
  EXPECT_EQ("30.", *northAxisString);

  OptionalDouble northAxis = building.getDouble(BuildingFields::NorthAxis);
  ASSERT_TRUE(northAxis);
  EXPECT_DOUBLE_EQ(30.0, *northAxis);

  OptionalString terrain = building.getString(BuildingFields::Terrain);
  ASSERT_TRUE(terrain);
  EXPECT_EQ("City", *terrain);

  OptionalUnsigned maximumNumberofWarmupDays = building.getUnsigned(BuildingFields::MaximumNumberofWarmupDays);
  ASSERT_TRUE(maximumNumberofWarmupDays);
  EXPECT_EQ(static_cast<unsigned>(25), *maximumNumberofWarmupDays);

}
  EXPECT_TRUE(handles[80] == newOrder[100]);
  EXPECT_TRUE(handles[100] == newOrder[80]);
}

TEST_F(IdfFixture,WorkspaceObjectOrder_ByIddObjectType) {
  Workspace workspace(IdfFixture::epIdfFile,openstudio::StrictnessLevel::Draft);
  WorkspaceObjectVector objectsInOriginalOrder = workspace.objects(true);

  WorkspaceObjectOrder wsOrder = workspace.order();
  IddObjectTypeVector orderByType;
  IntSet enumValues = IddObjectType::getValues();
  BOOST_FOREACH(int val,enumValues) {
    orderByType.push_back(IddObjectType(val));
  }
  wsOrder.setIddOrder(orderByType);
  WorkspaceObjectVector objectsInNewOrder = workspace.objects(true);
  EXPECT_EQ(objectsInOriginalOrder.size(),objectsInNewOrder.size());
  EXPECT_FALSE(objectsInOriginalOrder == objectsInNewOrder);

  // expect Materials before Constructions
  OptionalWorkspaceObject oMaterial;
  OptionalWorkspaceObject oConstruction;
  BOOST_FOREACH(const WorkspaceObject& object,objectsInNewOrder) {
    if (!oMaterial && (object.iddObject().type() == IddObjectType::Material)) {
      oMaterial = object;
      EXPECT_FALSE(oConstruction);
      OptionalUnsigned oIndex = wsOrder.indexInOrder(object.handle());
      EXPECT_FALSE(oIndex);
    }
    if (!oConstruction && (object.iddObject().type() == IddObjectType::Construction)) {
      oConstruction = object;