From 76724337bc18f9bdc65a7bf57b09e9233e103b73 Mon Sep 17 00:00:00 2001
From: Louis Amore <louis.amore@kitware.com>
Date: Fri, 25 Aug 2017 17:54:01 +0200
Subject: [PATCH] Add vtkXMLPTableWriter & vtkXMLPTableReader

---
 IO/ParallelXML/CMakeLists.txt                 |   1 +
 .../Testing/Python/testParallelXMLWriters.py  |  68 +-
 IO/ParallelXML/vtkXMLPTableWriter.cxx         | 527 +++++++++++++
 IO/ParallelXML/vtkXMLPTableWriter.h           | 229 ++++++
 IO/XML/CMakeLists.txt                         |   1 +
 IO/XML/vtkXMLPTableReader.cxx                 | 719 ++++++++++++++++++
 IO/XML/vtkXMLPTableReader.h                   | 228 ++++++
 7 files changed, 1757 insertions(+), 16 deletions(-)
 create mode 100644 IO/ParallelXML/vtkXMLPTableWriter.cxx
 create mode 100644 IO/ParallelXML/vtkXMLPTableWriter.h
 create mode 100644 IO/XML/vtkXMLPTableReader.cxx
 create mode 100644 IO/XML/vtkXMLPTableReader.h

diff --git a/IO/ParallelXML/CMakeLists.txt b/IO/ParallelXML/CMakeLists.txt
index 9c9a10b54ad..f32a47dc33a 100644
--- a/IO/ParallelXML/CMakeLists.txt
+++ b/IO/ParallelXML/CMakeLists.txt
@@ -6,6 +6,7 @@ set(Module_SRCS
   vtkXMLPRectilinearGridWriter.cxx
   vtkXMLPStructuredDataWriter.cxx
   vtkXMLPStructuredGridWriter.cxx
+  vtkXMLPTableWriter.cxx
   vtkXMLPUnstructuredDataWriter.cxx
   vtkXMLPUnstructuredGridWriter.cxx
   vtkXMLPHierarchicalBoxDataWriter.cxx
diff --git a/IO/ParallelXML/Testing/Python/testParallelXMLWriters.py b/IO/ParallelXML/Testing/Python/testParallelXMLWriters.py
index 68d9aff99ce..ca844d4d929 100644
--- a/IO/ParallelXML/Testing/Python/testParallelXMLWriters.py
+++ b/IO/ParallelXML/Testing/Python/testParallelXMLWriters.py
@@ -17,14 +17,18 @@ pf = vtk.vtkProgrammableFilter()
 def execute():
     info = pf.GetOutputInformation(0)
     et = vtk.vtkExtentTranslator()
-    et.SetWholeExtent(info.Get(vtk.vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT()))
-    et.SetPiece(info.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_PIECE_NUMBER()))
-    et.SetNumberOfPieces(info.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_NUMBER_OF_PIECES()))
-    et.PieceToExtent()
+
+    if pf.GetInput().IsA("vtkDataSet"):
+        et.SetWholeExtent(info.Get(vtk.vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT()))
+        et.SetPiece(info.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_PIECE_NUMBER()))
+        et.SetNumberOfPieces(info.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_NUMBER_OF_PIECES()))
+        et.PieceToExtent()
+
     output = pf.GetOutput()
     input = pf.GetInput()
     output.ShallowCopy(input)
-    output.Crop(et.GetExtent())
+    if pf.GetInput().IsA("vtkDataSet"):
+        output.Crop(et.GetExtent())
 
 pf.SetExecuteMethod(execute)
 
@@ -90,6 +94,26 @@ def GetSource(dataType):
         pf.SetInputData(sg)
         return pf
 
+    elif dataType == 'Table':
+        s.Update()
+        input = s.GetOutput()
+
+        table = vtk.vtkTable()
+        RTData = input.GetPointData().GetArray(0)
+        nbTuples = RTData.GetNumberOfTuples()
+
+        array = vtk.vtkFloatArray()
+        array.SetName("RTData")
+        array.SetNumberOfTuples(nbTuples)
+
+        for i in range(0, nbTuples):
+            array.SetTuple1(i, float(RTData.GetTuple1(i)))
+
+        table.AddColumn(array)
+
+        pf.SetInputData(table)
+        return pf
+
 def TestDataType(dataType, reader, writer, ext, numTris, useSubdir=False):
     s = GetSource(dataType)
 
@@ -114,17 +138,27 @@ def TestDataType(dataType, reader, writer, ext, numTris, useSubdir=False):
 
     reader.SetFileName(filename)
 
-    cf = vtk.vtkContourFilter()
-    cf.SetValue(0, 130)
-    cf.SetComputeNormals(0)
-    cf.SetComputeGradients(0)
-    cf.SetInputConnection(reader.GetOutputPort())
-    cf.UpdateInformation()
-    cf.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_NUMBER_OF_PIECES(), nranks)
-    cf.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_PIECE_NUMBER(), rank)
-    cf.Update()
-
-    ntris = cf.GetOutput().GetNumberOfCells()
+    ntris = 0
+
+    if dataType != "Table":
+        cf = vtk.vtkContourFilter()
+        cf.SetValue(0, 130)
+        cf.SetComputeNormals(0)
+        cf.SetComputeGradients(0)
+        cf.SetInputConnection(reader.GetOutputPort())
+        cf.UpdateInformation()
+        cf.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_NUMBER_OF_PIECES(), nranks)
+        cf.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_PIECE_NUMBER(), rank)
+        cf.Update()
+
+        ntris = cf.GetOutput().GetNumberOfCells()
+    else:
+        reader.UpdateInformation()
+        reader.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_NUMBER_OF_PIECES(), nranks)
+        reader.GetOutputInformation(0).Set(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_PIECE_NUMBER(), rank)
+        reader.Update()
+        ntris = reader.GetOutput().GetNumberOfRows()
+
     da = vtk.vtkIntArray()
     da.InsertNextValue(ntris)
 
@@ -148,9 +182,11 @@ TestDataType('ImageData', vtk.vtkXMLPImageDataReader(), vtk.vtkXMLPImageDataWrit
 TestDataType('RectilinearGrid', vtk.vtkXMLPRectilinearGridReader(), vtk.vtkXMLPRectilinearGridWriter(), 'vtr', 4924)
 TestDataType('StructuredGrid', vtk.vtkXMLPStructuredGridReader(), vtk.vtkXMLPStructuredGridWriter(), 'vts', 4924)
 TestDataType('UnstructuredGrid', vtk.vtkXMLPUnstructuredGridReader(), vtk.vtkXMLPUnstructuredGridWriter(), 'vtu', 11856)
+TestDataType('Table', vtk.vtkXMLPTableReader(), vtk.vtkXMLPTableWriter(), 'vtt', 18522)
 
 # Test writers with UseSubdirectory on
 TestDataType('ImageData', vtk.vtkXMLPImageDataReader(), vtk.vtkXMLPImageDataWriter(), 'vti', 4924, useSubdir=True)
 TestDataType('RectilinearGrid', vtk.vtkXMLPRectilinearGridReader(), vtk.vtkXMLPRectilinearGridWriter(), 'vtr', 4924, useSubdir=True)
 TestDataType('StructuredGrid', vtk.vtkXMLPStructuredGridReader(), vtk.vtkXMLPStructuredGridWriter(), 'vts', 4924, useSubdir=True)
 TestDataType('UnstructuredGrid', vtk.vtkXMLPUnstructuredGridReader(), vtk.vtkXMLPUnstructuredGridWriter(), 'vtu', 11856, useSubdir=True)
+TestDataType('Table', vtk.vtkXMLPTableReader(), vtk.vtkXMLPTableWriter(), 'vtt', 18522, useSubdir=True)
diff --git a/IO/ParallelXML/vtkXMLPTableWriter.cxx b/IO/ParallelXML/vtkXMLPTableWriter.cxx
new file mode 100644
index 00000000000..22d6b294d41
--- /dev/null
+++ b/IO/ParallelXML/vtkXMLPTableWriter.cxx
@@ -0,0 +1,527 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkXMLPTableWriter.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+#include "vtkXMLPTableWriter.h"
+
+#include "vtkCallbackCommand.h"
+#include "vtkDataSetAttributes.h"
+#include "vtkErrorCode.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkMultiProcessController.h"
+#include "vtkObjectFactory.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkTable.h"
+#include "vtkXMLTableWriter.h"
+
+#include <vtksys/SystemTools.hxx>
+
+#include <cassert>
+
+vtkStandardNewMacro(vtkXMLPTableWriter);
+vtkCxxSetObjectMacro(vtkXMLPTableWriter, Controller, vtkMultiProcessController);
+
+//----------------------------------------------------------------------------
+vtkXMLPTableWriter::vtkXMLPTableWriter()
+{
+  this->StartPiece = 0;
+  this->EndPiece = 0;
+  this->NumberOfPieces = 1;
+  this->GhostLevel = 0;
+  this->WriteSummaryFile = 1;
+
+  this->UseSubdirectory = false;
+
+  this->PathName = nullptr;
+  this->FileNameBase = nullptr;
+  this->FileNameExtension = nullptr;
+  this->PieceFileNameExtension = nullptr;
+
+  // Setup a callback for the internal writer to report progress.
+  this->InternalProgressObserver = vtkCallbackCommand::New();
+  this->InternalProgressObserver->SetCallback(&vtkXMLPTableWriter::ProgressCallbackFunction);
+  this->InternalProgressObserver->SetClientData(this);
+
+  this->Controller = nullptr;
+  this->SetController(vtkMultiProcessController::GetGlobalController());
+
+  this->ContinuingExecution = false;
+  this->CurrentPiece = -1;
+  this->PieceWrittenFlags = nullptr;
+}
+
+//----------------------------------------------------------------------------
+vtkXMLPTableWriter::~vtkXMLPTableWriter()
+{
+  delete[] this->PathName;
+  delete[] this->FileNameBase;
+  delete[] this->FileNameExtension;
+  delete[] this->PieceFileNameExtension;
+  delete[] this->PieceWrittenFlags;
+  this->SetController(nullptr);
+  this->InternalProgressObserver->Delete();
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os, indent);
+  os << indent << "NumberOfPieces: " << this->NumberOfPieces << "\n";
+  os << indent << "StartPiece: " << this->StartPiece << "\n";
+  os << indent << "EndPiece: " << this->EndPiece << "\n";
+  os << indent << "GhostLevel: " << this->GhostLevel << "\n";
+  os << indent << "WriteSummaryFile: " << this->WriteSummaryFile << "\n";
+}
+
+//----------------------------------------------------------------------------
+vtkTable* vtkXMLPTableWriter::GetInput()
+{
+  return vtkTable::SafeDownCast(this->Superclass::GetInput());
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::ProcessRequest(
+  vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
+{
+  if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_EXTENT()))
+  {
+    return this->RequestUpdateExtent(request, inputVector, outputVector);
+  }
+
+  int retVal = this->Superclass::ProcessRequest(request, inputVector, outputVector);
+  if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA()))
+  {
+    if (retVal && this->ContinuingExecution)
+    {
+      request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 1);
+    }
+    else
+    {
+      request->Remove(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING());
+      this->ContinuingExecution = false;
+    }
+  }
+  return retVal;
+}
+
+//----------------------------------------------------------------------------
+const char* vtkXMLPTableWriter::GetDataSetName()
+{
+  return "PTable";
+}
+
+//----------------------------------------------------------------------------
+const char* vtkXMLPTableWriter::GetDefaultFileExtension()
+{
+  return "pvtt";
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::SetWriteSummaryFile(int flag)
+{
+  vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting WriteSummaryFile to "
+                << flag);
+  if (this->WriteSummaryFile != flag)
+  {
+    this->WriteSummaryFile = flag;
+    this->Modified();
+  }
+}
+
+//----------------------------------------------------------------------------
+vtkXMLWriter* vtkXMLPTableWriter::CreatePieceWriter(int index)
+{
+  // Create the writer for the piece.
+  vtkXMLTableWriter* pWriter = vtkXMLTableWriter::New();
+  pWriter->SetInputConnection(this->GetInputConnection(0, 0));
+  pWriter->SetNumberOfPieces(this->NumberOfPieces);
+  pWriter->SetWritePiece(index);
+
+  return pWriter;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::WritePData(vtkIndent indent)
+{
+  vtkTable* input = this->GetInput();
+  this->WritePRowData(input->GetRowData(), indent);
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::RequestUpdateExtent(vtkInformation* vtkNotUsed(request),
+  vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector))
+{
+  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
+  int piece = 0;
+  if (this->ContinuingExecution)
+  {
+    assert(this->CurrentPiece >= this->StartPiece && this->CurrentPiece <= this->EndPiece &&
+      this->CurrentPiece < this->NumberOfPieces);
+    piece = this->CurrentPiece;
+  }
+  else
+  {
+    piece = this->StartPiece;
+  }
+
+  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), piece);
+  inInfo->Set(
+    vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), this->GetNumberOfPieces());
+  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), this->GhostLevel);
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::WriteInternal()
+{
+  bool beginning = (this->ContinuingExecution == false);
+  bool end = true;
+
+  this->ContinuingExecution = false;
+  this->CurrentPiece = beginning ? this->StartPiece : this->CurrentPiece;
+
+  assert(this->CurrentPiece >= this->StartPiece && this->CurrentPiece <= this->EndPiece);
+  end = (this->CurrentPiece == this->EndPiece);
+
+  if (beginning)
+  {
+    // Prepare the file name.
+    this->SplitFileName();
+    delete[] this->PieceWrittenFlags;
+    this->PieceWrittenFlags = new unsigned char[this->NumberOfPieces];
+    memset(this->PieceWrittenFlags, 0, sizeof(unsigned char) * this->NumberOfPieces);
+
+    // Prepare the extension.
+    this->SetupPieceFileNameExtension();
+  }
+
+  // Write the current piece.
+
+  // Split progress range by piece.  Just assume all pieces are the
+  // same size.
+  float progressRange[2] = { 0.f, 0.f };
+  this->GetProgressRange(progressRange);
+
+  this->SetProgressRange(
+    progressRange, this->CurrentPiece - this->StartPiece, this->EndPiece - this->StartPiece + 1);
+  vtkTable* inputTable = this->GetInput();
+  if (inputTable && inputTable->GetNumberOfRows() > 0)
+  {
+    if (!this->WritePiece(this->CurrentPiece))
+    {
+      vtkErrorMacro("Could not write the current piece.");
+      this->DeleteFiles();
+      return 0;
+    }
+    this->PieceWrittenFlags[this->CurrentPiece] = static_cast<unsigned char>(0x1);
+  }
+
+  // Write the summary file if requested.
+  if (end && this->WriteSummaryFile)
+  {
+    // Decide whether to write the summary file.
+    bool writeSummaryLocally =
+      (this->Controller == nullptr || this->Controller->GetLocalProcessId() == 0);
+
+    // Let subclasses collect information, if any to write the summary file.
+    this->PrepareSummaryFile();
+
+    if (writeSummaryLocally)
+    {
+      if (!this->Superclass::WriteInternal())
+      {
+        vtkErrorMacro("Ran out of disk space; deleting file(s) already written");
+        this->DeleteFiles();
+        return 0;
+      }
+    }
+  }
+
+  if (end == false)
+  {
+    this->CurrentPiece++;
+    assert(this->CurrentPiece <= this->EndPiece);
+    this->ContinuingExecution = true;
+  }
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::PrepareSummaryFile()
+{
+  if (this->Controller && this->Controller->GetNumberOfProcesses() > 1)
+  {
+    assert(this->PieceWrittenFlags != nullptr);
+    // Reduce information about which pieces were written out to rank 0.
+    int myId = this->Controller->GetLocalProcessId();
+    unsigned char* recvBuffer = (myId == 0) ? new unsigned char[this->NumberOfPieces] : nullptr;
+    this->Controller->Reduce(
+      this->PieceWrittenFlags, recvBuffer, this->NumberOfPieces, vtkCommunicator::MAX_OP, 0);
+    if (myId == 0)
+    {
+      std::swap(this->PieceWrittenFlags, recvBuffer);
+    }
+    delete[] recvBuffer;
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::WritePrimaryElementAttributes(std::ostream& os, vtkIndent indent)
+{
+  this->WriteScalarAttribute("GhostLevel", this->GhostLevel);
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::WriteData()
+{
+  // Write the summary file.
+  ostream& os = *(this->Stream);
+  vtkIndent indent = vtkIndent().GetNextIndent();
+  vtkIndent nextIndent = indent.GetNextIndent();
+
+  this->StartFile();
+  if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
+  {
+    return 0;
+  }
+
+  os << indent << "<" << this->GetDataSetName();
+  this->WritePrimaryElementAttributes(os, indent);
+  if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
+  {
+    return 0;
+  }
+  os << ">\n";
+
+  // Write the information needed for a reader to produce the output's
+  // information during UpdateInformation without reading a piece.
+  this->WritePData(indent.GetNextIndent());
+  if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
+  {
+    return 0;
+  }
+
+  // Write the elements referencing each piece and its file.
+  for (int i = 0; i < this->NumberOfPieces; ++i)
+  {
+    if (this->PieceWrittenFlags[i] == 0)
+    {
+      continue;
+    }
+    os << nextIndent << "<Piece";
+    this->WritePPieceAttributes(i);
+    if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
+    {
+      return 0;
+    }
+    os << "/>\n";
+  }
+
+  os << indent << "</" << this->GetDataSetName() << ">\n";
+
+  this->EndFile();
+  return (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) ? 0 : 1;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::WritePPieceAttributes(int index)
+{
+  char* fileName = this->CreatePieceFileName(index);
+  this->WriteStringAttribute("Source", fileName);
+  delete[] fileName;
+}
+
+//----------------------------------------------------------------------------
+char* vtkXMLPTableWriter::CreatePieceFileName(int index, const char* path)
+{
+  std::ostringstream s;
+  if (path)
+  {
+    s << path;
+  }
+  s << this->FileNameBase;
+  if (this->UseSubdirectory)
+  {
+    s << "/" << this->FileNameBase;
+  }
+  s << "_" << index;
+  if (this->PieceFileNameExtension)
+  {
+    s << this->PieceFileNameExtension;
+  }
+
+  size_t len = s.str().length();
+  char* buffer = new char[len + 1];
+  strncpy(buffer, s.str().c_str(), len);
+  buffer[len] = '\0';
+
+  return buffer;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::SplitFileName()
+{
+  // Split the FileName into its PathName, FileNameBase, and
+  // FileNameExtension components.
+
+  std::string pathname = vtksys::SystemTools::GetProgramPath(this->FileName);
+  // Pathname may be empty if FileName is simply a filename without any leading
+  // "/".
+  if (!pathname.empty())
+  {
+    pathname += "/";
+  }
+  std::string filename_wo_ext = vtksys::SystemTools::GetFilenameWithoutExtension(this->FileName);
+  std::string ext = vtksys::SystemTools::GetFilenameExtension(this->FileName);
+
+  delete[] this->PathName;
+  delete[] this->FileNameBase;
+  delete[] this->FileNameExtension;
+
+  this->PathName = vtksys::SystemTools::DuplicateString(pathname.c_str());
+  this->FileNameBase = vtksys::SystemTools::DuplicateString(filename_wo_ext.c_str());
+  this->FileNameExtension = vtksys::SystemTools::DuplicateString(ext.c_str());
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::WritePiece(int index)
+{
+  // Create the writer for the piece.  Its configuration should match
+  // our own writer.
+  vtkXMLWriter* pWriter = this->CreatePieceWriter(index);
+  pWriter->AddObserver(vtkCommand::ProgressEvent, this->InternalProgressObserver);
+
+  char* fileName = this->CreatePieceFileName(index, this->PathName);
+  std::string path = vtksys::SystemTools::GetParentDirectory(fileName);
+  if (!path.empty() && !vtksys::SystemTools::PathExists(path))
+  {
+    vtksys::SystemTools::MakeDirectory(path);
+  }
+  pWriter->SetFileName(fileName);
+  delete[] fileName;
+
+  // Copy the writer settings.
+  pWriter->SetDebug(this->Debug);
+  pWriter->SetCompressor(this->Compressor);
+  pWriter->SetDataMode(this->DataMode);
+  pWriter->SetByteOrder(this->ByteOrder);
+  pWriter->SetEncodeAppendedData(this->EncodeAppendedData);
+  pWriter->SetHeaderType(this->HeaderType);
+  pWriter->SetBlockSize(this->BlockSize);
+
+  // Write the piece.
+  int result = pWriter->Write();
+  this->SetErrorCode(pWriter->GetErrorCode());
+
+  // Cleanup.
+  pWriter->RemoveObserver(this->InternalProgressObserver);
+  pWriter->Delete();
+
+  return result;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::ProgressCallbackFunction(
+  vtkObject* caller, unsigned long, void* clientdata, void*)
+{
+  vtkAlgorithm* w = vtkAlgorithm::SafeDownCast(caller);
+  if (w)
+  {
+    reinterpret_cast<vtkXMLPTableWriter*>(clientdata)->ProgressCallback(w);
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::ProgressCallback(vtkAlgorithm* w)
+{
+  float width = this->ProgressRange[1] - this->ProgressRange[0];
+  float internalProgress = w->GetProgress();
+  float progress = this->ProgressRange[0] + internalProgress * width;
+  this->UpdateProgressDiscrete(progress);
+  if (this->AbortExecute)
+  {
+    w->SetAbortExecute(1);
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::WritePRowData(vtkDataSetAttributes* ds, vtkIndent indent)
+{
+  if (ds->GetNumberOfArrays() == 0)
+  {
+    return;
+  }
+  ostream& os = *(this->Stream);
+  char** names = this->CreateStringArray(ds->GetNumberOfArrays());
+
+  os << indent << "<PRowData";
+  this->WriteAttributeIndices(ds, names);
+  if (this->ErrorCode != vtkErrorCode::NoError)
+  {
+    this->DestroyStringArray(ds->GetNumberOfArrays(), names);
+    return;
+  }
+  os << ">\n";
+
+  for (int i = 0; i < ds->GetNumberOfArrays(); ++i)
+  {
+    this->WritePArray(ds->GetAbstractArray(i), indent.GetNextIndent(), names[i]);
+    if (this->ErrorCode != vtkErrorCode::NoError)
+    {
+      this->DestroyStringArray(ds->GetNumberOfArrays(), names);
+      return;
+    }
+  }
+
+  os << indent << "</PRowData>\n";
+  os.flush();
+  if (os.fail())
+  {
+    this->SetErrorCode(vtkErrorCode::GetLastSystemError());
+  }
+
+  this->DestroyStringArray(ds->GetNumberOfArrays(), names);
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::DeleteFiles()
+{
+  for (int i = this->StartPiece; i < this->EndPiece; ++i)
+  {
+    char* fileName = this->CreatePieceFileName(i, this->PathName);
+    this->DeleteAFile(fileName);
+    delete[] fileName;
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableWriter::SetupPieceFileNameExtension()
+{
+  delete[] this->PieceFileNameExtension;
+
+  // Create a temporary piece writer and then initialize the extension.
+  vtkXMLWriter* writer = this->CreatePieceWriter(0);
+  const char* ext = writer->GetDefaultFileExtension();
+  this->PieceFileNameExtension = new char[strlen(ext) + 2];
+  this->PieceFileNameExtension[0] = '.';
+  strcpy(this->PieceFileNameExtension + 1, ext);
+  writer->Delete();
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableWriter::FillInputPortInformation(int, vtkInformation* info)
+{
+  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkTable");
+  return 1;
+}
diff --git a/IO/ParallelXML/vtkXMLPTableWriter.h b/IO/ParallelXML/vtkXMLPTableWriter.h
new file mode 100644
index 00000000000..12f2166ba3c
--- /dev/null
+++ b/IO/ParallelXML/vtkXMLPTableWriter.h
@@ -0,0 +1,229 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkXMLPTableWriter.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+/**
+ * @class   vtkXMLPTableWriter
+ * @brief   Write PVTK XML UnstructuredGrid files.
+ *
+ * vtkXMLPTableWriter writes the PVTK XML Table
+ * file format.  One table input can be written into a
+ * parallel file format with any number of pieces spread across files.
+ * The standard extension for this writer's file format is "pvtt".
+ * This writer uses vtkXMLTableWriter to write the
+ * individual piece files.
+ *
+ * @sa
+ * vtkXMLTableWriter
+*/
+
+#ifndef vtkXMLPTableWriter_h
+#define vtkXMLPTableWriter_h
+
+#include "vtkIOParallelXMLModule.h" // For export macro
+#include "vtkXMLWriter.h"
+
+class vtkCallbackCommand;
+class vtkMultiProcessController;
+class vtkTable;
+class vtkXMLTableWriter;
+
+class VTKIOPARALLELXML_EXPORT vtkXMLPTableWriter : public vtkXMLWriter
+{
+public:
+  static vtkXMLPTableWriter* New();
+  vtkTypeMacro(vtkXMLPTableWriter, vtkXMLWriter);
+  void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE;
+
+  /**
+   * Get/Set the writer's input.
+   */
+  vtkTable* GetInput();
+
+  /**
+   * Get the default file extension for files written by this writer.
+   */
+  const char* GetDefaultFileExtension() VTK_OVERRIDE;
+
+  //@{
+  /**
+   * Get/Set the number of pieces that are being written in parallel.
+   */
+  vtkSetMacro(NumberOfPieces, int);
+  vtkGetMacro(NumberOfPieces, int);
+  //@}
+
+  //@{
+  /**
+   * Get/Set the range of pieces assigned to this writer.
+   */
+  vtkSetMacro(StartPiece, int);
+  vtkGetMacro(StartPiece, int);
+  vtkSetMacro(EndPiece, int);
+  vtkGetMacro(EndPiece, int);
+  //@}
+
+  //@{
+  /**
+   * Get/Set the ghost level used for this writer's piece.
+   */
+  vtkSetMacro(GhostLevel, int);
+  vtkGetMacro(GhostLevel, int);
+  //@}
+
+  //@{
+  /**
+   * Get/Set whether to use a subdirectory to store the pieces
+   */
+  vtkSetMacro(UseSubdirectory, bool);
+  vtkGetMacro(UseSubdirectory, bool);
+  //@}
+
+  //@{
+  /**
+   * Get/Set whether the writer should write the summary file that
+   * refers to all of the pieces' individual files.
+   * This is on by default. Note that only the first process writes
+   * the summary file.
+   */
+  virtual void SetWriteSummaryFile(int flag);
+  vtkGetMacro(WriteSummaryFile, int);
+  vtkBooleanMacro(WriteSummaryFile, int);
+  //@}
+
+  //@{
+  /**
+   * Controller used to communicate data type of blocks.
+   * By default, the global controller is used. If you want another
+   * controller to be used, set it with this.
+   */
+  virtual void SetController(vtkMultiProcessController*);
+  vtkGetObjectMacro(Controller, vtkMultiProcessController);
+  //@}
+
+  /**
+   * Overridden to handle passing the CONTINUE_EXECUTING() flags to the
+   * executive.
+   */
+  int ProcessRequest(vtkInformation* request, vtkInformationVector** inputVector,
+    vtkInformationVector* outputVector) VTK_OVERRIDE;
+
+protected:
+  vtkXMLPTableWriter();
+  ~vtkXMLPTableWriter() VTK_OVERRIDE;
+
+  /**
+  * see algorithm for more info
+  */
+  int FillInputPortInformation(int port, vtkInformation* info) VTK_OVERRIDE;
+
+  const char* GetDataSetName() VTK_OVERRIDE;
+  vtkXMLTableWriter* CreateTablePieceWriter();
+
+  vtkXMLWriter* CreatePieceWriter(int index);
+  void WritePData(vtkIndent indent);
+
+  /**
+   * Overridden to make appropriate piece request from upstream.
+   */
+  virtual int RequestUpdateExtent(vtkInformation* request, vtkInformationVector** inputVector,
+    vtkInformationVector* outputVector);
+  /**
+  * Override writing method from superclass.
+  */
+  int WriteInternal() VTK_OVERRIDE;
+
+  /**
+  * Collect information between ranks before writing the summary file.
+  * This method is called on all ranks while summary file is only written on 1
+  * rank (rank 0).
+  */
+  virtual void PrepareSummaryFile();
+
+  void WritePrimaryElementAttributes(ostream& os, vtkIndent indent) VTK_OVERRIDE;
+  int WriteData() VTK_OVERRIDE;
+  virtual void WritePPieceAttributes(int index);
+
+  char* CreatePieceFileName(int index, const char* path = nullptr);
+  void SplitFileName();
+  virtual int WritePiece(int index);
+
+  /**
+  * Callback registered with the InternalProgressObserver.
+  */
+  static void ProgressCallbackFunction(vtkObject*, unsigned long, void*, void*);
+
+  /**
+   * Valid at end of WriteInternal to indicate if we're going to continue
+   * execution.
+   */
+  vtkGetMacro(ContinuingExecution, bool);
+
+  void WritePRowData(vtkDataSetAttributes* ds, vtkIndent indent);
+  /**
+  * Progress callback from internal writer.
+  */
+
+  /**
+  * The observer to report progress from the internal writer.
+  */
+  vtkCallbackCommand* InternalProgressObserver;
+
+  vtkMultiProcessController* Controller;
+
+  virtual void ProgressCallback(vtkAlgorithm* w);
+
+  int StartPiece;
+  int EndPiece;
+  int NumberOfPieces;
+  int GhostLevel;
+  int WriteSummaryFile;
+  bool UseSubdirectory;
+
+  char* PathName;
+  char* FileNameBase;
+  char* FileNameExtension;
+  char* PieceFileNameExtension;
+
+private:
+  vtkXMLPTableWriter(const vtkXMLPTableWriter&) VTK_DELETE_FUNCTION;
+  void operator=(const vtkXMLPTableWriter&) VTK_DELETE_FUNCTION;
+
+  /**
+   * Method used to delete all written files.
+   */
+  void DeleteFiles();
+
+  /**
+   * Initializes PieceFileNameExtension.
+   */
+  void SetupPieceFileNameExtension();
+
+  /**
+  * Indicates the piece currently being written.
+  */
+  int CurrentPiece;
+
+  /**
+  * Set in WriteInternal() to request continued execution from the executive to
+  * write more pieces.
+  */
+  bool ContinuingExecution;
+
+  /**
+  * Flags used to keep track of which pieces were written out.
+  */
+  unsigned char* PieceWrittenFlags;
+};
+
+#endif
diff --git a/IO/XML/CMakeLists.txt b/IO/XML/CMakeLists.txt
index 71afbfe8b47..5d5e2155a43 100644
--- a/IO/XML/CMakeLists.txt
+++ b/IO/XML/CMakeLists.txt
@@ -25,6 +25,7 @@ set(Module_SRCS
   vtkXMLPRectilinearGridReader.cxx
   vtkXMLPStructuredDataReader.cxx
   vtkXMLPStructuredGridReader.cxx
+  vtkXMLPTableReader.cxx
   vtkXMLPUnstructuredDataReader.cxx
   vtkXMLPUnstructuredGridReader.cxx
   vtkXMLReader.cxx
diff --git a/IO/XML/vtkXMLPTableReader.cxx b/IO/XML/vtkXMLPTableReader.cxx
new file mode 100644
index 00000000000..0fed2279664
--- /dev/null
+++ b/IO/XML/vtkXMLPTableReader.cxx
@@ -0,0 +1,719 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkXMLPTableReader.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+#include "vtkXMLPTableReader.h"
+
+#include "vtkCallbackCommand.h"
+#include "vtkCellArray.h"
+#include "vtkDataArraySelection.h"
+#include "vtkDataSetAttributes.h"
+#include "vtkXMLDataElement.h"
+#include "vtkIdTypeArray.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkObjectFactory.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkTable.h"
+#include "vtkXMLTableReader.h"
+
+#include <cassert>
+#include <sstream>
+
+vtkStandardNewMacro(vtkXMLPTableReader);
+
+//----------------------------------------------------------------------------
+vtkXMLPTableReader::vtkXMLPTableReader()
+{
+  this->NumberOfPieces = 0;
+
+  this->PieceElements = nullptr;
+  this->PieceReaders = nullptr;
+  this->CanReadPieceFlag = nullptr;
+
+  this->PathName = nullptr;
+
+  this->TotalNumberOfRows = 0;
+
+  // Setup a callback for the internal serial readers to report
+  // progress.
+  this->PieceProgressObserver = vtkCallbackCommand::New();
+  this->PieceProgressObserver->SetCallback(&vtkXMLPTableReader::PieceProgressCallbackFunction);
+  this->PieceProgressObserver->SetClientData(this);
+
+  this->ColumnSelection = vtkDataArraySelection::New();
+  this->ColumnSelection->AddObserver(vtkCommand::ModifiedEvent, this->SelectionObserver);
+}
+
+//----------------------------------------------------------------------------
+vtkXMLPTableReader::~vtkXMLPTableReader()
+{
+  if (this->NumberOfPieces)
+  {
+    this->DestroyPieces();
+  }
+  delete[] this->PathName;
+  this->PieceProgressObserver->Delete();
+
+  this->ColumnSelection->RemoveObserver(this->SelectionObserver);
+  this->ColumnSelection->Delete();
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::CopyOutputInformation(vtkInformation* outInfo, int port)
+{
+  vtkInformation* localInfo = this->GetExecutive()->GetOutputInformation(port);
+
+  if (localInfo->Has(CAN_HANDLE_PIECE_REQUEST()))
+  {
+    outInfo->CopyEntry(localInfo, CAN_HANDLE_PIECE_REQUEST());
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os, indent);
+
+  os << indent << "ColumnSelection: " << this->ColumnSelection << "\n";
+}
+
+//----------------------------------------------------------------------------
+vtkTable* vtkXMLPTableReader::GetOutput()
+{
+  return this->GetOutput(0);
+}
+
+//----------------------------------------------------------------------------
+vtkTable* vtkXMLPTableReader::GetOutput(int idx)
+{
+  return vtkTable::SafeDownCast(this->GetOutputDataObject(idx));
+}
+
+//----------------------------------------------------------------------------
+const char* vtkXMLPTableReader::GetDataSetName()
+{
+  return "PTable";
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::GetOutputUpdateExtent(int& piece, int& numberOfPieces)
+{
+  vtkInformation* outInfo = this->GetCurrentOutputInformation();
+  piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
+  numberOfPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupOutputTotals()
+{
+  this->TotalNumberOfRows = 0;
+  for (int i = this->StartPiece; i < this->EndPiece; ++i)
+  {
+    if (this->PieceReaders[i])
+    {
+      this->TotalNumberOfRows += this->PieceReaders[i]->GetNumberOfRows();
+    }
+  }
+  this->StartRow = 0;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupOutputData()
+{
+  this->Superclass::SetupOutputData();
+
+  // Setup the output arrays.
+  vtkTable* output = vtkTable::SafeDownCast(this->GetCurrentOutput());
+  vtkDataSetAttributes* rowData = output->GetRowData();
+
+  // Get the size of the output arrays.
+  unsigned long rowTuples = this->GetNumberOfRows();
+
+  // Allocate data in the arrays.
+  if (this->PRowElement)
+  {
+    for (int i = 0; i < this->PRowElement->GetNumberOfNestedElements(); ++i)
+    {
+      vtkXMLDataElement* eNested = this->PRowElement->GetNestedElement(i);
+      if (this->ColumnIsEnabled(eNested))
+      {
+        vtkAbstractArray* array = this->CreateArray(eNested);
+        if (array)
+        {
+          array->SetNumberOfTuples(rowTuples);
+          rowData->AddArray(array);
+          array->Delete();
+        }
+        else
+        {
+          this->DataError = 1;
+        }
+      }
+    }
+  }
+
+  // Setup attribute indices for the point data and cell data.
+  this->ReadAttributeIndices(this->PRowElement, rowData);
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadPieceData(int index)
+{
+  this->Piece = index;
+
+  // We need data, make sure the piece can be read.
+  if (!this->CanReadPiece(this->Piece))
+  {
+    vtkErrorMacro("File for piece " << this->Piece << " cannot be read.");
+    return 0;
+  }
+
+  // Actually read the data.
+  this->PieceReaders[this->Piece]->SetAbortExecute(0);
+  vtkDataArraySelection* pds = this->PieceReaders[this->Piece]->GetPointDataArraySelection();
+  vtkDataArraySelection* cds = this->PieceReaders[this->Piece]->GetCellDataArraySelection();
+  pds->CopySelections(this->PointDataArraySelection);
+  cds->CopySelections(this->CellDataArraySelection);
+  return this->ReadPieceData();
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::CanReadPiece(int index)
+{
+  // If necessary, test whether the piece can be read.
+  vtkXMLTableReader* reader = this->PieceReaders[index];
+  if (reader && !this->CanReadPieceFlag[index])
+  {
+    if (reader->CanReadFile(reader->GetFileName()))
+    {
+      // We can read the piece.  Save result to avoid later repeat of
+      // test.
+      this->CanReadPieceFlag[index] = 1;
+    }
+    else
+    {
+      // We cannot read the piece.  Destroy the reader to avoid later
+      // repeat of test.
+      this->PieceReaders[index] = nullptr;
+      reader->Delete();
+    }
+  }
+
+  return (this->PieceReaders[index] ? 1 : 0);
+}
+
+//----------------------------------------------------------------------------
+char* vtkXMLPTableReader::CreatePieceFileName(const char* fileName)
+{
+  assert(fileName);
+
+  std::ostringstream fn_with_warning_C4701;
+
+  // only prepend the path if the given file name is not
+  // absolute (i.e. doesn't start with '/')
+  if (this->PathName && fileName[0] != '/')
+  {
+    fn_with_warning_C4701 << this->PathName;
+  }
+  fn_with_warning_C4701 << fileName;
+
+  size_t len = fn_with_warning_C4701.str().length();
+  char* buffer = new char[len + 1];
+  strncpy(buffer, fn_with_warning_C4701.str().c_str(), len);
+  buffer[len] = '\0';
+
+  return buffer;
+}
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SplitFileName()
+{
+  if (!this->FileName)
+  {
+    vtkErrorMacro(<< "Need to specify a filename");
+    return;
+  }
+
+  // Pull the PathName component out of the FileName.
+  size_t length = strlen(this->FileName);
+  char* fileName = new char[length + 1];
+  strcpy(fileName, this->FileName);
+  char* begin = fileName;
+  char* end = fileName + length;
+  char* s;
+
+#if defined(_WIN32)
+  // Convert to UNIX-style slashes.
+  for (s = begin; s != end; ++s)
+  {
+    if (*s == '\\')
+    {
+      *s = '/';
+    }
+  }
+#endif
+
+  // Extract the path name up to the last '/'.
+  delete[] this->PathName;
+  this->PathName = nullptr;
+  char* rbegin = end - 1;
+  char* rend = begin - 1;
+  for (s = rbegin; s != rend; --s)
+  {
+    if (*s == '/')
+    {
+      break;
+    }
+  }
+  if (s >= begin)
+  {
+    length = (s - begin) + 1;
+    this->PathName = new char[length + 1];
+    strncpy(this->PathName, this->FileName, length);
+    this->PathName[length] = '\0';
+  }
+
+  // Cleanup temporary name.
+  delete[] fileName;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::PieceProgressCallbackFunction(
+  vtkObject*, unsigned long, void* clientdata, void*)
+{
+  reinterpret_cast<vtkXMLPTableReader*>(clientdata)->PieceProgressCallback();
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::PieceProgressCallback()
+{
+  float width = this->ProgressRange[1] - this->ProgressRange[0];
+  float pieceProgress = this->PieceReaders[this->Piece]->GetProgress();
+  float progress = this->ProgressRange[0] + pieceProgress * width;
+  this->UpdateProgressDiscrete(progress);
+  if (this->AbortExecute)
+  {
+    this->PieceReaders[this->Piece]->SetAbortExecute(1);
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupNextPiece()
+{
+  if (this->PieceReaders[this->Piece])
+  {
+    this->StartRow += this->PieceReaders[this->Piece]->GetNumberOfRows();
+  }
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadPieceData()
+{
+  // Use the internal reader to read the piece.
+  this->PieceReaders[this->Piece]->UpdatePiece(0, 1, 0);
+
+  vtkTable* input = this->GetPieceInputAsTable(this->Piece);
+  vtkTable* output = vtkTable::SafeDownCast(this->GetCurrentOutput());
+
+  // If there are some points, but no PPoints element, report the
+  // error.
+  if (!this->PRowElement && (this->GetNumberOfRows() > 0))
+  {
+    vtkErrorMacro("Could not find PRows element with 1 array.");
+    return 0;
+  }
+
+  if (!input->GetRowData())
+  {
+    return 0;
+  }
+
+  // copy any row data
+  if (input->GetRowData())
+  {
+    int i;
+    for (i = 0; i < input->GetRowData()->GetNumberOfArrays(); i++)
+    {
+      if (this->ColumnSelection->ArrayIsEnabled(input->GetRowData()->GetArrayName(i)))
+      {
+        output->GetRowData()->AddArray(input->GetRowData()->GetArray(i));
+      }
+    }
+  }
+
+  // copy any field data
+  if (input->GetFieldData())
+  {
+    int i;
+    for (i = 0; i < input->GetFieldData()->GetNumberOfArrays(); i++)
+    {
+      output->GetFieldData()->AddArray(input->GetFieldData()->GetArray(i));
+    }
+  }
+
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+vtkXMLTableReader* vtkXMLPTableReader::CreatePieceReader()
+{
+  return vtkXMLTableReader::New();
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::FillOutputPortInformation(int, vtkInformation* info)
+{
+  info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkTable");
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::RequestInformation(
+  vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
+{
+  vtkInformation* outInfo = outputVector->GetInformationObject(0);
+  outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
+  return this->Superclass::RequestInformation(request, inputVector, outputVector);
+}
+
+//----------------------------------------------------------------------------
+vtkTable* vtkXMLPTableReader::GetOutputAsTable()
+{
+  return vtkTable::SafeDownCast(this->GetOutputDataObject(0));
+}
+
+//----------------------------------------------------------------------------
+vtkTable* vtkXMLPTableReader::GetPieceInputAsTable(int piece)
+{
+  vtkXMLTableReader* reader = this->PieceReaders[piece];
+  if (!reader || reader->GetNumberOfOutputPorts() < 1)
+  {
+    return nullptr;
+  }
+  return static_cast<vtkTable*>(reader->GetExecutive()->GetOutputData(0));
+}
+
+//----------------------------------------------------------------------------
+vtkIdType vtkXMLPTableReader::GetNumberOfRows()
+{
+  return this->TotalNumberOfRows;
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupEmptyOutput()
+{
+  this->GetCurrentOutput()->Initialize();
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupOutputInformation(vtkInformation* outInfo)
+{
+  if (this->InformationError)
+  {
+    vtkErrorMacro("Should not still be processing output information if have set InformationError");
+    return;
+  }
+
+  // Initialize DataArraySelections to enable all that are present
+  this->SetDataArraySelections(this->PRowElement, this->ColumnSelection);
+
+  // Setup the Field Information for RowData.  We only need the
+  // information from one piece because all pieces have the same set of arrays.
+  vtkInformationVector* infoVector = nullptr;
+  if (!this->SetFieldDataInfo(this->PRowElement, vtkDataObject::FIELD_ASSOCIATION_ROWS,
+        this->GetNumberOfRows(), infoVector))
+  {
+    return;
+  }
+  if (infoVector)
+  {
+    infoVector->Delete();
+  }
+
+  outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::ReadXMLData()
+{
+  // Get the update request.
+  vtkInformation* outInfo = this->GetCurrentOutputInformation();
+  int piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
+  int numberOfPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
+
+  vtkDebugMacro("Updating piece " << piece << " of " << numberOfPieces);
+
+  // Setup the range of pieces that will be read.
+  this->SetupUpdateExtent(piece, numberOfPieces);
+
+  // If there are no data to read, stop now.
+  if (this->StartPiece == this->EndPiece)
+  {
+    return;
+  }
+
+  vtkDebugMacro(
+    "Reading piece range [" << this->StartPiece << ", " << this->EndPiece << ") from file.");
+
+  // Let superclasses read data.  This also allocates output data.
+  this->Superclass::ReadXMLData();
+
+  // Split current progress range based on fraction contributed by
+  // each piece.
+  float progressRange[2] = { 0.f, 0.f };
+  this->GetProgressRange(progressRange);
+
+  // Calculate the cumulative fraction of data contributed by each
+  // piece (for progress).
+  std::vector<float> fractions(this->EndPiece - this->StartPiece + 1);
+  fractions[0] = 0;
+  for (int i = this->StartPiece; i < this->EndPiece; ++i)
+  {
+    int index = i - this->StartPiece;
+    fractions[index + 1] = (fractions[index] + this->GetNumberOfRowsInPiece(i));
+  }
+  if (fractions[this->EndPiece - this->StartPiece] == 0)
+  {
+    fractions[this->EndPiece - this->StartPiece] = 1;
+  }
+  for (int i = this->StartPiece; i < this->EndPiece; ++i)
+  {
+    int index = i - this->StartPiece;
+    fractions[index + 1] = fractions[index + 1] / fractions[this->EndPiece - this->StartPiece];
+  }
+
+  // Read the data needed from each piece.
+  for (int i = this->StartPiece; (i < this->EndPiece && !this->AbortExecute && !this->DataError);
+       ++i)
+  {
+    // Set the range of progress for this piece.
+    this->SetProgressRange(progressRange, i - this->StartPiece, fractions.data());
+
+    if (!this->ReadPieceData(i))
+    {
+      // An error occurred while reading the piece.
+      this->DataError = 1;
+    }
+    this->SetupNextPiece();
+  }
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadPrimaryElement(vtkXMLDataElement* ePrimary)
+{
+
+  if (!this->Superclass::ReadPrimaryElement(ePrimary))
+  {
+    return 0;
+  }
+
+  // Read information about the pieces.
+  this->PRowElement = nullptr;
+  int i;
+  int numNested = ePrimary->GetNumberOfNestedElements();
+  int numPieces = 0;
+  for (i = 0; i < numNested; ++i)
+  {
+    vtkXMLDataElement* eNested = ePrimary->GetNestedElement(i);
+    if (strcmp(eNested->GetName(), "Piece") == 0)
+    {
+      ++numPieces;
+    }
+    else if (strcmp(eNested->GetName(), "PRowData") == 0)
+    {
+      this->PRowElement = eNested;
+    }
+  }
+  this->SetupPieces(numPieces);
+  int piece = 0;
+  for (i = 0; i < numNested; ++i)
+  {
+    vtkXMLDataElement* eNested = ePrimary->GetNestedElement(i);
+    if (strcmp(eNested->GetName(), "Piece") == 0)
+    {
+      if (!this->ReadPiece(eNested, piece++))
+      {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+
+void vtkXMLPTableReader::SetupUpdateExtent(int piece, int numberOfPieces)
+{
+  this->UpdatePiece = piece;
+  this->UpdateNumberOfPieces = numberOfPieces;
+
+  // If more pieces are requested than available, just return empty
+  // pieces for the extra ones.s
+  if (this->UpdateNumberOfPieces > this->NumberOfPieces)
+  {
+    this->UpdateNumberOfPieces = this->NumberOfPieces;
+  }
+
+  // Find the range of pieces to read.
+  if (this->UpdatePiece < this->UpdateNumberOfPieces)
+  {
+    this->StartPiece = ((this->UpdatePiece * this->NumberOfPieces) / this->UpdateNumberOfPieces);
+    this->EndPiece =
+      (((this->UpdatePiece + 1) * this->NumberOfPieces) / this->UpdateNumberOfPieces);
+  }
+  else
+  {
+    this->StartPiece = 0;
+    this->EndPiece = 0;
+  }
+
+  // Update the information of the pieces we need.
+  for (int i = this->StartPiece; i < this->EndPiece; ++i)
+  {
+    if (this->CanReadPiece(i))
+    {
+      this->PieceReaders[i]->UpdateInformation();
+      vtkXMLTableReader* pReader = this->PieceReaders[i];
+      pReader->SetupUpdateExtent(0, 1);
+    }
+  }
+
+  // Find the total size of the output.
+  this->SetupOutputTotals();
+}
+
+//----------------------------------------------------------------------------
+vtkIdType vtkXMLPTableReader::GetNumberOfRowsInPiece(int piece)
+{
+  return this->PieceReaders[piece] ? this->PieceReaders[piece]->GetNumberOfRows() : 0;
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadXMLInformation()
+{
+  // First setup the filename components.
+  this->SplitFileName();
+
+  // Now proceed with reading the information.
+  return this->Superclass::ReadXMLInformation();
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetupPieces(int numPieces)
+{
+  if (this->NumberOfPieces)
+  {
+    this->DestroyPieces();
+  }
+  this->NumberOfPieces = numPieces;
+  this->PieceElements = new vtkXMLDataElement*[this->NumberOfPieces];
+  this->PieceReaders = new vtkXMLTableReader*[this->NumberOfPieces];
+  this->CanReadPieceFlag = new int[this->NumberOfPieces];
+  int i;
+  for (i = 0; i < this->NumberOfPieces; ++i)
+  {
+    this->PieceElements[i] = nullptr;
+    this->PieceReaders[i] = nullptr;
+    this->CanReadPieceFlag[i] = 0;
+  }
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::DestroyPieces()
+{
+  int i;
+  for (i = 0; i < this->NumberOfPieces; ++i)
+  {
+    if (this->PieceReaders[i])
+    {
+      this->PieceReaders[i]->RemoveObserver(this->PieceProgressObserver);
+      this->PieceReaders[i]->Delete();
+    }
+  }
+  delete[] this->PieceElements;
+  delete[] this->CanReadPieceFlag;
+  delete[] this->PieceReaders;
+  this->PieceElements = nullptr;
+  this->PieceReaders = nullptr;
+  this->NumberOfPieces = 0;
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadPiece(vtkXMLDataElement* ePiece, int index)
+{
+  this->Piece = index;
+  return this->ReadPiece(ePiece);
+}
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ReadPiece(vtkXMLDataElement* ePiece)
+{
+  this->PieceElements[this->Piece] = ePiece;
+
+  const char* fileName = ePiece->GetAttribute("Source");
+  if (!fileName)
+  {
+    vtkErrorMacro("Piece " << this->Piece << " has no Source attribute.");
+    return 0;
+  }
+
+  // The file name is relative to the summary file.  Convert it to
+  // something we can use.
+  char* pieceFileName = this->CreatePieceFileName(fileName);
+
+  vtkXMLTableReader* reader = this->CreatePieceReader();
+  this->PieceReaders[this->Piece] = reader;
+  this->PieceReaders[this->Piece]->AddObserver(
+    vtkCommand::ProgressEvent, this->PieceProgressObserver);
+  reader->SetFileName(pieceFileName);
+
+  delete[] pieceFileName;
+
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::ColumnIsEnabled(vtkXMLDataElement* elementRowData)
+{
+  const char* name = elementRowData->GetAttribute("Name");
+  return (name && this->ColumnSelection->ArrayIsEnabled(name));
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::GetNumberOfColumnArrays()
+{
+  return this->ColumnSelection->GetNumberOfArrays();
+}
+
+//----------------------------------------------------------------------------
+const char* vtkXMLPTableReader::GetColumnArrayName(int index)
+{
+  return this->ColumnSelection->GetArrayName(index);
+}
+
+//----------------------------------------------------------------------------
+int vtkXMLPTableReader::GetColumnArrayStatus(const char* name)
+{
+  return this->ColumnSelection->ArrayIsEnabled(name);
+}
+
+//----------------------------------------------------------------------------
+void vtkXMLPTableReader::SetColumnArrayStatus(const char* name, int status)
+{
+  if (status)
+  {
+    this->ColumnSelection->EnableArray(name);
+  }
+  else
+  {
+    this->ColumnSelection->DisableArray(name);
+  }
+}
diff --git a/IO/XML/vtkXMLPTableReader.h b/IO/XML/vtkXMLPTableReader.h
new file mode 100644
index 00000000000..9d9b3d49536
--- /dev/null
+++ b/IO/XML/vtkXMLPTableReader.h
@@ -0,0 +1,228 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkXMLPTableReader.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+/**
+ * @class   vtkXMLPTableReader
+ * @brief   Read PVTK XML Table files.
+ *
+ * vtkXMLPTableReader reads the PVTK XML Table
+ * file format.  This reads the parallel format's summary file and
+ * then uses vtkXMLTableReader to read data from the
+ * individual Table piece files.  Streaming is supported.
+ * The standard extension for this reader's file format is "pvtt".
+ *
+ * @sa
+ * vtkXMLTableReader
+*/
+
+#ifndef vtkXMLPTableReader_h
+#define vtkXMLPTableReader_h
+
+#include "vtkIOXMLModule.h" // For export macro
+#include "vtkXMLReader.h"
+
+//class vtkDataArray;
+class vtkTable;
+class vtkXMLTableReader;
+
+class VTKIOXML_EXPORT vtkXMLPTableReader : public vtkXMLReader
+{
+public:
+  vtkTypeMacro(vtkXMLPTableReader,vtkXMLReader);
+  void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE;
+  static vtkXMLPTableReader *New();
+
+  //@{
+  /**
+   * Get the reader's output.
+   */
+  vtkTable *GetOutput();
+  vtkTable *GetOutput(int idx);
+  //@}
+
+  /**
+  * For the specified port, copy the information this reader sets up in
+  * SetupOutputInformation to outInfo
+  */
+  void CopyOutputInformation(vtkInformation *outInfo, int port) VTK_OVERRIDE;
+
+  //@{
+  /**
+   * Get the number of pieces from the summary file being read.
+   */
+  vtkGetMacro(NumberOfPieces, int);
+  //@}
+
+  //@{
+  /**
+   * Get the number of columns arrays available in the input.
+   */
+  int GetNumberOfColumnArrays();
+  //@}
+
+  //@{
+  /**
+   * Get the name of the column with the given index in
+   * the input.
+   */
+  const char* GetColumnArrayName(int index);
+  //@}
+
+  //@{
+  /**
+   * Get/Set whether the column array with the given name is to
+   * be read.
+   */
+  int GetColumnArrayStatus(const char* name);
+  void SetColumnArrayStatus(const char* name, int status);
+  //@}
+
+  //@{
+  /**
+   * Get the data array selection tables used to configure which data
+   * arrays are loaded by the reader.
+   */
+  vtkGetObjectMacro(ColumnSelection, vtkDataArraySelection);
+  //@}
+
+protected:
+  vtkXMLPTableReader();
+  ~vtkXMLPTableReader() VTK_OVERRIDE;
+
+  const char* GetDataSetName() VTK_OVERRIDE;
+  void GetOutputUpdateExtent(int& piece, int& numberOfPieces);
+  void SetupOutputTotals();
+
+  void SetupOutputData() VTK_OVERRIDE;
+  void SetupNextPiece();
+  int ReadPieceData();
+
+  vtkXMLTableReader *CreatePieceReader();
+  int FillOutputPortInformation(int, vtkInformation*) VTK_OVERRIDE;
+
+  int RequestInformation(vtkInformation *request,
+                                 vtkInformationVector **inputVector,
+                                 vtkInformationVector *outputVector) VTK_OVERRIDE;
+
+
+  vtkTable* GetOutputAsTable();
+  vtkTable* GetPieceInputAsTable(int piece);
+  vtkIdType GetNumberOfRows();
+
+  void SetupEmptyOutput() VTK_OVERRIDE;
+
+  /**
+  * Setup the output's information.
+  */
+  void SetupOutputInformation(vtkInformation *outInfo) VTK_OVERRIDE;
+
+  /**
+  * Pipeline execute data driver.  Called by vtkXMLReader.
+  */
+  void ReadXMLData() VTK_OVERRIDE;
+  int ReadPrimaryElement(vtkXMLDataElement* ePrimary) VTK_OVERRIDE;
+  void SetupUpdateExtent(int piece, int numberOfPieces);
+
+  /**
+  * Get the number of rows in the given piece.  Valid after
+  * UpdateInformation.
+  */
+  virtual vtkIdType GetNumberOfRowsInPiece(int piece);
+
+  /**
+  * The update request.
+  */
+  int UpdatePiece;
+  int UpdateNumberOfPieces;
+
+  /**
+  * The range of pieces from the file that will form the UpdatePiece.
+  */
+  int StartPiece;
+  int EndPiece;
+  vtkIdType TotalNumberOfRows;
+  vtkIdType StartRow;
+
+  /**
+  * Pipeline execute information driver.  Called by vtkXMLReader.
+  */
+  int ReadXMLInformation() VTK_OVERRIDE;
+
+  virtual void SetupPieces(int numPieces);
+  virtual void DestroyPieces();
+  int ReadPiece(vtkXMLDataElement* ePiece, int index);
+  virtual int ReadPiece(vtkXMLDataElement* ePiece);
+  int ReadPieceData(int index);
+  int CanReadPiece(int index);
+
+  char* CreatePieceFileName(const char* fileName);
+  void SplitFileName();
+
+  /**
+  * Callback registered with the PieceProgressObserver.
+  */
+  static void PieceProgressCallbackFunction(vtkObject*, unsigned long, void*,
+                                           void*);
+  virtual void PieceProgressCallback();
+
+  /**
+  * Pieces from the input summary file.
+  */
+  int NumberOfPieces;
+
+  /**
+  * The piece currently being read.
+  */
+  int Piece;
+
+  /**
+  * The path to the input file without the file name.
+  */
+  char* PathName;
+
+  /**
+  * Information per-piece.
+  */
+  vtkXMLDataElement** PieceElements;
+  vtkXMLTableReader** PieceReaders;
+  int* CanReadPieceFlag;
+
+  /**
+  * The PRowData element representations.
+  */
+  vtkXMLDataElement* PRowElement;
+
+  /**
+  * The observer to report progress from reading serial data in each
+  * piece.
+  */
+  vtkCallbackCommand* PieceProgressObserver;
+
+  /**
+  * Check whether the given array element is an enabled array.
+  */
+  int ColumnIsEnabled(vtkXMLDataElement* elementRowData);
+
+  int GetNumberOfRowArrays();
+  const char* GetRowArrayName(int index);
+
+
+  vtkDataArraySelection* ColumnSelection;
+
+private:
+  vtkXMLPTableReader(const vtkXMLPTableReader&) VTK_DELETE_FUNCTION;
+  void operator=(const vtkXMLPTableReader&) VTK_DELETE_FUNCTION;
+};
+
+#endif
-- 
GitLab