<commit_before>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/
#include "LoadDataReaction.h"

#include "ActiveObjects.h"
#include "DataSource.h"
#include "EmdFormat.h"
#include "ModuleManager.h"
#include "Pipeline.h"
#include "PipelineManager.h"
#include "RAWFileReaderDialog.h"
#include "RecentFilesMenu.h"
#include "Utilities.h"

#include <pqActiveObjects.h>
#include <pqLoadDataReaction.h>
#include <pqPipelineSource.h>
#include <pqProxyWidgetDialog.h>
#include <pqRenderView.h>
#include <pqSMAdaptor.h>
#include <pqView.h>
#include <vtkSMCoreUtilities.h>
#include <vtkSMParaViewPipelineController.h>
#include <vtkSMPropertyHelper.h>
#include <vtkSMSessionProxyManager.h>
#include <vtkSMSourceProxy.h>
#include <vtkSMStringVectorProperty.h>
#include <vtkSMViewProxy.h>

#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkTIFFReader.h>
#include <vtkTrivialProducer.h>

#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>
#include <QJsonArray>
#include <QMessageBox>

#include <sstream>

namespace {
bool hasData(vtkSMProxy* reader)
{
  vtkSMSourceProxy* dataSource = vtkSMSourceProxy::SafeDownCast(reader);
  if (!dataSource) {
    return false;
  }

  dataSource->UpdatePipeline();
  vtkAlgorithm* vtkalgorithm =
    vtkAlgorithm::SafeDownCast(dataSource->GetClientSideObject());
  if (!vtkalgorithm) {
    return false;
  }

  // Create a clone and release the reader data.
  vtkImageData* data =
    vtkImageData::SafeDownCast(vtkalgorithm->GetOutputDataObject(0));
  if (!data) {
    return false;
  }

  int extent[6];
  data->GetExtent(extent);
  if (extent[0] > extent[1] || extent[2] > extent[3] || extent[4] > extent[5]) {
    return false;
  }

  vtkPointData* pd = data->GetPointData();
  if (!pd) {
    return false;
  }

  if (pd->GetNumberOfArrays() < 1) {
    return false;
  }
  return true;
}
}

namespace tomviz {

LoadDataReaction::LoadDataReaction(QAction* parentObject)
  : pqReaction(parentObject)
{
}

LoadDataReaction::~LoadDataReaction() = default;

void LoadDataReaction::onTriggered()
{
  loadData();
}

QList<DataSource*> LoadDataReaction::loadData()
{
  QStringList filters;
  filters
    << "Common file types (*.emd *.jpg *.jpeg *.png *.tiff *.tif *.raw"
       " *.dat *.bin *.txt *.mhd *.mha *.vti *.mrc *.st *.rec *.ali *.xmf *.xdmf)"
    << "EMD (*.emd)"
    << "JPeg Image files (*.jpg *.jpeg)"
    << "PNG Image files (*.png)"
    << "TIFF Image files (*.tiff *.tif)"
    << "OME-TIFF Image files (*.ome.tif)"
    << "Raw data files (*.raw *.dat *.bin)"
    << "Meta Image files (*.mhd *.mha)"
    << "VTK ImageData Files (*.vti)"
    << "MRC files (*.mrc *.st *.rec *.ali)"
    << "XDMF files (*.xmf *.xdmf)"
    << "Text files (*.txt)"
    << "All files (*.*)";

  QFileDialog dialog(nullptr);
  dialog.setFileMode(QFileDialog::ExistingFiles);
  dialog.setNameFilters(filters);
  dialog.setObjectName("FileOpenDialog-tomviz"); // avoid name collision?

  QList<DataSource*> dataSources;
  if (dialog.exec()) {
    QStringList filenames = dialog.selectedFiles();
    dataSources << loadData(filenames);
  }

  return dataSources;
}

DataSource* LoadDataReaction::loadData(const QString& fileName,
                                       bool defaultModules, bool addToRecent,
                                       bool child, const QJsonObject& options)
{
  QJsonObject opts = options;
  opts["defaultModules"] = defaultModules;
  opts["addToRecent"] = addToRecent;
  opts["child"] = child;
  return LoadDataReaction::loadData(fileName, opts);
}

DataSource* LoadDataReaction::loadData(const QString& fileName,
                                       const QJsonObject& val)
{
  QStringList fileNames;
  fileNames << fileName;

  return loadData(fileNames, val);
}

DataSource* LoadDataReaction::loadData(const QStringList& fileNames,
                                       const QJsonObject& options)
{
  bool defaultModules = options["defaultModules"].toBool(true);
  bool addToRecent = options["addToRecent"].toBool(true);
  bool child = options["child"].toBool(false);
  bool loadWithParaview = false;

  DataSource* dataSource(nullptr);
  QString fileName;
  if (fileNames.size() > 0) {
    fileName = fileNames[0];
  }
  QFileInfo info(fileName);
  if (info.suffix().toLower() == "emd") {
    // Load the file using our simple EMD class.
    EmdFormat emdFile;
    vtkNew<vtkImageData> imageData;
    if (emdFile.read(fileName.toLatin1().data(), imageData)) {
      dataSource = new DataSource(imageData);
      LoadDataReaction::dataSourceAdded(dataSource, defaultModules, child);
    }
  } else if (info.completeSuffix().endsWith("ome.tif")) {
    auto pxm = tomviz::ActiveObjects::instance().proxyManager();
    const char* name = "OMETIFFReader";
    vtkSmartPointer<vtkSMProxy> source;
    source.TakeReference(pxm->NewProxy("sources", name));
    QString pname = vtkSMCoreUtilities::GetFileNameProperty(source);
    vtkSMStringVectorProperty* prop = vtkSMStringVectorProperty::SafeDownCast(
      source->GetProperty(pname.toUtf8().data()));
    pqSMAdaptor::setElementProperty(prop, fileName);
    source->UpdateVTKObjects();

    dataSource = createDataSource(source, defaultModules, child);
    QJsonObject readerProperties;
    readerProperties["name"] = name;
    dataSource->setReaderProperties(readerProperties.toVariantMap());
  } else if (options.contains("reader")) {
    // Create the ParaView reader and set its properties using the JSON
    // configuration.
    auto props = options["reader"].toObject();
    auto name = props["name"].toString();

    auto pxm = ActiveObjects::instance().proxyManager();
    vtkSmartPointer<vtkSMProxy> reader;
    reader.TakeReference(pxm->NewProxy("sources", name.toLatin1().data()));

    setProperties(props, reader);
    setFileNameProperties(props, reader);
    reader->UpdateVTKObjects();
    vtkSMSourceProxy::SafeDownCast(reader)->UpdatePipelineInformation();
    dataSource =
      LoadDataReaction::createDataSource(reader, defaultModules, child);
    if (dataSource == nullptr) {
      return nullptr;
    }

    dataSource->setReaderProperties(props.toVariantMap());

  } else if (info.suffix().toLower() == "tiff" ||
             info.suffix().toLower() == "tif") {
    if (fileNames.size() > 1) {
      // Ensure all the images in the stack have the same size.
      QList<ImageFileInfo> summary;
      if (!loadTiffStack(fileNames, summary)) {
        badStackAlert(summary);
        return nullptr;
      }
    }
    loadWithParaview = true;
  } else {
    loadWithParaview = true;
  }

  if (loadWithParaview) {
    // Use ParaView's file load infrastructure.
    pqPipelineSource* reader = pqLoadDataReaction::loadData(fileNames);
    if (!reader) {
      return nullptr;
    }

    dataSource = createDataSource(reader->getProxy(), defaultModules, child);
    if (dataSource == nullptr) {
      return nullptr;
    }

    QJsonObject props = readerProperties(reader->getProxy());
    props["name"] = reader->getProxy()->GetXMLName();

    dataSource->setReaderProperties(props.toVariantMap());

    vtkNew<vtkSMParaViewPipelineController> controller;
    controller->UnRegisterProxy(reader->getProxy());
  }

  // It is possible that the dataSource will be null if, for example, loading
  // a VTI is cancelled in the array selection dialog. Guard against this.
  if (!dataSource) {
    return nullptr;
  }

  // Now for house keeping, registering elements, etc.
  dataSource->setFileName(fileName);
  if (fileNames.size() > 1) {
    dataSource->setFileNames(fileNames);
  }
  if (addToRecent && dataSource) {
    RecentFilesMenu::pushDataReader(dataSource);
  }
  return dataSource;
}

bool LoadDataReaction::loadTiffStack(const QStringList& fileNames,
                                     QList<ImageFileInfo>& summary)
{

  vtkNew<vtkTIFFReader> reader;
  int n = -1;
  int m = -1;
  int dims[3];
  int i = -1;
  bool success = true;
  foreach (QString file, fileNames) {
    i++;
    reader->SetFileName(file.toLatin1().data());
    reader->Update();
    reader->GetOutput()->GetDimensions(dims);
    summary.push_back(ImageFileInfo());
    summary[i].m = dims[0];
    summary[i].n = dims[1];
    summary[i].fileName = file;
    summary[i].consistent = true;

    if (n == -1 && m == -1) {
      n = dims[0];
      m = dims[1];
    } else {
      if (n != dims[0] || m != dims[1]) {
        summary[i].consistent = false;
        success = false;
        // In the future all the files will be read to allow the user to
        // fix/exclude specific files.
        // But for now, just quit at the first inconsistency in the stack.
        return success;
      }
    }
  }
  return success;
}

void LoadDataReaction::badStackAlert(QList<ImageFileInfo>& summary)
{
  // for (auto it = summary.begin(); it != summary.end(); ++it) {
  foreach (ImageFileInfo image, summary) {
    if (!image.consistent) {
      QMessageBox::warning(
        tomviz::mainWidget(), "Error",
        QString("The dimensions of the images in this stack are inconsistent.\n"
                "This error first occurred at the file:\n\n"
                "%1\n\n"
                "The expected size is (%2, %3), but got (%4, %5) instead.")
          .arg(image.fileName)
          .arg(summary[0].m)
          .arg(summary[0].n)
          .arg(image.m)
          .arg(image.n));
      return;
    }
  }
  return;
}

DataSource* LoadDataReaction::createDataSource(vtkSMProxy* reader,
                                               bool defaultModules, bool child)
{
  // Prompt user for reader configuration, unless it is TIFF.
  QScopedPointer<QDialog> dialog(new pqProxyWidgetDialog(reader));
  bool hasVisibleWidgets =
    qobject_cast<pqProxyWidgetDialog*>(dialog.data())->hasVisibleWidgets();
  if (QString(reader->GetXMLName()) == "TVRawImageReader") {
    dialog.reset(new RAWFileReaderDialog(reader));
    hasVisibleWidgets = true;
    // This will show only a partial filename when reading multiple files as a
    // raw image
    vtkSMPropertyHelper fname(reader, "FilePrefix");
    QFileInfo info(fname.GetAsString());
    dialog->setWindowTitle(QString("Opening %1").arg(info.fileName()));
  } else {
    dialog->setWindowTitle("Configure Reader Parameters");
  }
  dialog->setObjectName("ConfigureReaderDialog");
  if (QString(reader->GetXMLName()) == "TIFFSeriesReader" ||
      hasVisibleWidgets == false || dialog->exec() == QDialog::Accepted) {

    if (!hasData(reader)) {
      qCritical() << "Error: failed to load file!";
      return nullptr;
    }

    auto source = vtkSMSourceProxy::SafeDownCast(reader);
    source->UpdatePipeline();
    auto algo = vtkAlgorithm::SafeDownCast(source->GetClientSideObject());
    auto data = algo->GetOutputDataObject(0);
    auto image = vtkImageData::SafeDownCast(data);

    DataSource* dataSource = new DataSource(image);
    // Do whatever we need to do with a new data source.
    LoadDataReaction::dataSourceAdded(dataSource, defaultModules, child);
    return dataSource;
  }
  return nullptr;
}

void LoadDataReaction::dataSourceAdded(DataSource* dataSource,
                                       bool defaultModules, bool child)
{
  if (!dataSource) {
    return;
  }
  DataSource* previousActiveDataSource =
    ActiveObjects::instance().activeDataSource();
  bool oldMoveObjectsEnabled = ActiveObjects::instance().moveObjectsEnabled();
  ActiveObjects::instance().setMoveObjectsMode(false);
  if (child) {
    ModuleManager::instance().addChildDataSource(dataSource);
  } else {
    auto pipeline = new Pipeline(dataSource);
    PipelineManager::instance().addPipeline(pipeline);
    // TODO Eventually we shouldn't need to keep track of the data sources,
    // the pipeline should do that for us.
    ModuleManager::instance().addDataSource(dataSource);
    if (defaultModules) {
      pipeline->addDefaultModules(dataSource);
    }
  }

  // Work through pathological cases as necessary, prefer active view.
  ActiveObjects::instance().createRenderViewIfNeeded();
  auto view = ActiveObjects::instance().activeView();

  if (!view || QString(view->GetXMLName()) != "RenderView") {
    ActiveObjects::instance().setActiveViewToFirstRenderView();
    view = ActiveObjects::instance().activeView();
  }

  ActiveObjects::instance().setMoveObjectsMode(oldMoveObjectsEnabled);
  if (!previousActiveDataSource) {
    pqRenderView* renderView =
      qobject_cast<pqRenderView*>(pqActiveObjects::instance().activeView());
    if (renderView) {
      tomviz::createCameraOrbit(dataSource->proxy(),
                                renderView->getRenderViewProxy());
    }
  }
}

QJsonObject LoadDataReaction::readerProperties(vtkSMProxy* reader)
{
  QStringList propNames({ "DataScalarType", "DataByteOrder",
                          "NumberOfScalarComponents", "DataExtent" });

  QJsonObject props;
  foreach (QString propName, propNames) {
    auto prop = reader->GetProperty(propName.toLatin1().data());
    if (prop != nullptr) {
      props[propName] = toJson(prop);
    }
  }

  // Special case file name related properties
  auto prop = reader->GetProperty("FileName");
  if (prop != nullptr) {
    props["fileName"] = toJson(prop);
  }
  prop = reader->GetProperty("FileNames");
  if (prop != nullptr) {
    auto fileNames = toJson(prop).toArray();
    if (fileNames.size() > 1) {
      props["fileNames"] = fileNames;
    }
    // Normalize to fileNames for single value.
    else {
      props["fileName"] = fileNames[0];
    }
  }
  prop = reader->GetProperty("FilePrefix");
  if (prop != nullptr) {
    props["fileName"] = toJson(prop);
  }

  return props;
}

void LoadDataReaction::setFileNameProperties(const QJsonObject& props,
                                             vtkSMProxy* reader)
{
  auto prop = reader->GetProperty("FileName");
  if (prop != nullptr) {
    if (!props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' property.";
      return;
    }
    tomviz::setProperty(props["fileName"], prop);
  }
  prop = reader->GetProperty("FileNames");
  if (prop != nullptr) {
    if (!props.contains("fileNames") && !props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' or 'fileNames' property.";
      return;
    }

    if (props.contains("fileNames")) {
      tomviz::setProperty(props["fileNames"], prop);
    } else {
      QJsonArray fileNames;
      fileNames.append(props["fileName"]);
      tomviz::setProperty(fileNames, prop);
    }
  }
  prop = reader->GetProperty("FilePrefix");
  if (prop != nullptr) {
    if (!props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' property.";
      return;
    }
    tomviz::setProperty(props["fileName"], prop);
  }
}

} // end of namespace tomviz
<commit_msg>Simplify the logic of loadWithParaview<commit_after>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/
#include "LoadDataReaction.h"

#include "ActiveObjects.h"
#include "DataSource.h"
#include "EmdFormat.h"
#include "ModuleManager.h"
#include "Pipeline.h"
#include "PipelineManager.h"
#include "RAWFileReaderDialog.h"
#include "RecentFilesMenu.h"
#include "Utilities.h"

#include <pqActiveObjects.h>
#include <pqLoadDataReaction.h>
#include <pqPipelineSource.h>
#include <pqProxyWidgetDialog.h>
#include <pqRenderView.h>
#include <pqSMAdaptor.h>
#include <pqView.h>
#include <vtkSMCoreUtilities.h>
#include <vtkSMParaViewPipelineController.h>
#include <vtkSMPropertyHelper.h>
#include <vtkSMSessionProxyManager.h>
#include <vtkSMSourceProxy.h>
#include <vtkSMStringVectorProperty.h>
#include <vtkSMViewProxy.h>

#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkTIFFReader.h>
#include <vtkTrivialProducer.h>

#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>
#include <QJsonArray>
#include <QMessageBox>

#include <sstream>

namespace {
bool hasData(vtkSMProxy* reader)
{
  vtkSMSourceProxy* dataSource = vtkSMSourceProxy::SafeDownCast(reader);
  if (!dataSource) {
    return false;
  }

  dataSource->UpdatePipeline();
  vtkAlgorithm* vtkalgorithm =
    vtkAlgorithm::SafeDownCast(dataSource->GetClientSideObject());
  if (!vtkalgorithm) {
    return false;
  }

  // Create a clone and release the reader data.
  vtkImageData* data =
    vtkImageData::SafeDownCast(vtkalgorithm->GetOutputDataObject(0));
  if (!data) {
    return false;
  }

  int extent[6];
  data->GetExtent(extent);
  if (extent[0] > extent[1] || extent[2] > extent[3] || extent[4] > extent[5]) {
    return false;
  }

  vtkPointData* pd = data->GetPointData();
  if (!pd) {
    return false;
  }

  if (pd->GetNumberOfArrays() < 1) {
    return false;
  }
  return true;
}
}

namespace tomviz {

LoadDataReaction::LoadDataReaction(QAction* parentObject)
  : pqReaction(parentObject)
{
}

LoadDataReaction::~LoadDataReaction() = default;

void LoadDataReaction::onTriggered()
{
  loadData();
}

QList<DataSource*> LoadDataReaction::loadData()
{
  QStringList filters;
  filters
    << "Common file types (*.emd *.jpg *.jpeg *.png *.tiff *.tif *.raw"
       " *.dat *.bin *.txt *.mhd *.mha *.vti *.mrc *.st *.rec *.ali *.xmf *.xdmf)"
    << "EMD (*.emd)"
    << "JPeg Image files (*.jpg *.jpeg)"
    << "PNG Image files (*.png)"
    << "TIFF Image files (*.tiff *.tif)"
    << "OME-TIFF Image files (*.ome.tif)"
    << "Raw data files (*.raw *.dat *.bin)"
    << "Meta Image files (*.mhd *.mha)"
    << "VTK ImageData Files (*.vti)"
    << "MRC files (*.mrc *.st *.rec *.ali)"
    << "XDMF files (*.xmf *.xdmf)"
    << "Text files (*.txt)"
    << "All files (*.*)";

  QFileDialog dialog(nullptr);
  dialog.setFileMode(QFileDialog::ExistingFiles);
  dialog.setNameFilters(filters);
  dialog.setObjectName("FileOpenDialog-tomviz"); // avoid name collision?

  QList<DataSource*> dataSources;
  if (dialog.exec()) {
    QStringList filenames = dialog.selectedFiles();
    dataSources << loadData(filenames);
  }

  return dataSources;
}

DataSource* LoadDataReaction::loadData(const QString& fileName,
                                       bool defaultModules, bool addToRecent,
                                       bool child, const QJsonObject& options)
{
  QJsonObject opts = options;
  opts["defaultModules"] = defaultModules;
  opts["addToRecent"] = addToRecent;
  opts["child"] = child;
  return LoadDataReaction::loadData(fileName, opts);
}

DataSource* LoadDataReaction::loadData(const QString& fileName,
                                       const QJsonObject& val)
{
  QStringList fileNames;
  fileNames << fileName;

  return loadData(fileNames, val);
}

DataSource* LoadDataReaction::loadData(const QStringList& fileNames,
                                       const QJsonObject& options)
{
  bool defaultModules = options["defaultModules"].toBool(true);
  bool addToRecent = options["addToRecent"].toBool(true);
  bool child = options["child"].toBool(false);
  bool loadWithParaview = true;

  DataSource* dataSource(nullptr);
  QString fileName;
  if (fileNames.size() > 0) {
    fileName = fileNames[0];
  }
  QFileInfo info(fileName);
  if (info.suffix().toLower() == "emd") {
    // Load the file using our simple EMD class.
    loadWithParaview = false;
    EmdFormat emdFile;
    vtkNew<vtkImageData> imageData;
    if (emdFile.read(fileName.toLatin1().data(), imageData)) {
      dataSource = new DataSource(imageData);
      LoadDataReaction::dataSourceAdded(dataSource, defaultModules, child);
    }
  } else if (info.completeSuffix().endsWith("ome.tif")) {
    loadWithParaview = false;
    auto pxm = tomviz::ActiveObjects::instance().proxyManager();
    const char* name = "OMETIFFReader";
    vtkSmartPointer<vtkSMProxy> source;
    source.TakeReference(pxm->NewProxy("sources", name));
    QString pname = vtkSMCoreUtilities::GetFileNameProperty(source);
    vtkSMStringVectorProperty* prop = vtkSMStringVectorProperty::SafeDownCast(
      source->GetProperty(pname.toUtf8().data()));
    pqSMAdaptor::setElementProperty(prop, fileName);
    source->UpdateVTKObjects();

    dataSource = createDataSource(source, defaultModules, child);
    QJsonObject readerProperties;
    readerProperties["name"] = name;
    dataSource->setReaderProperties(readerProperties.toVariantMap());
  } else if (options.contains("reader")) {
    loadWithParaview = false;
    // Create the ParaView reader and set its properties using the JSON
    // configuration.
    auto props = options["reader"].toObject();
    auto name = props["name"].toString();

    auto pxm = ActiveObjects::instance().proxyManager();
    vtkSmartPointer<vtkSMProxy> reader;
    reader.TakeReference(pxm->NewProxy("sources", name.toLatin1().data()));

    setProperties(props, reader);
    setFileNameProperties(props, reader);
    reader->UpdateVTKObjects();
    vtkSMSourceProxy::SafeDownCast(reader)->UpdatePipelineInformation();
    dataSource =
      LoadDataReaction::createDataSource(reader, defaultModules, child);
    if (dataSource == nullptr) {
      return nullptr;
    }

    dataSource->setReaderProperties(props.toVariantMap());

  } else if (info.suffix().toLower() == "tiff" ||
             info.suffix().toLower() == "tif") {
    if (fileNames.size() > 1) {
      // Ensure all the images in the stack have the same size.
      QList<ImageFileInfo> summary;
      if (!loadTiffStack(fileNames, summary)) {
        badStackAlert(summary);
        return nullptr;
      }
    }
  }

  if (loadWithParaview) {
    // Use ParaView's file load infrastructure.
    pqPipelineSource* reader = pqLoadDataReaction::loadData(fileNames);
    if (!reader) {
      return nullptr;
    }

    dataSource = createDataSource(reader->getProxy(), defaultModules, child);
    if (dataSource == nullptr) {
      return nullptr;
    }

    QJsonObject props = readerProperties(reader->getProxy());
    props["name"] = reader->getProxy()->GetXMLName();

    dataSource->setReaderProperties(props.toVariantMap());

    vtkNew<vtkSMParaViewPipelineController> controller;
    controller->UnRegisterProxy(reader->getProxy());
  }

  // It is possible that the dataSource will be null if, for example, loading
  // a VTI is cancelled in the array selection dialog. Guard against this.
  if (!dataSource) {
    return nullptr;
  }

  // Now for house keeping, registering elements, etc.
  dataSource->setFileName(fileName);
  if (fileNames.size() > 1) {
    dataSource->setFileNames(fileNames);
  }
  if (addToRecent && dataSource) {
    RecentFilesMenu::pushDataReader(dataSource);
  }
  return dataSource;
}

bool LoadDataReaction::loadTiffStack(const QStringList& fileNames,
                                     QList<ImageFileInfo>& summary)
{

  vtkNew<vtkTIFFReader> reader;
  int n = -1;
  int m = -1;
  int dims[3];
  int i = -1;
  bool success = true;
  foreach (QString file, fileNames) {
    i++;
    reader->SetFileName(file.toLatin1().data());
    reader->Update();
    reader->GetOutput()->GetDimensions(dims);
    summary.push_back(ImageFileInfo());
    summary[i].m = dims[0];
    summary[i].n = dims[1];
    summary[i].fileName = file;
    summary[i].consistent = true;

    if (n == -1 && m == -1) {
      n = dims[0];
      m = dims[1];
    } else {
      if (n != dims[0] || m != dims[1]) {
        summary[i].consistent = false;
        success = false;
        // In the future all the files will be read to allow the user to
        // fix/exclude specific files.
        // But for now, just quit at the first inconsistency in the stack.
        return success;
      }
    }
  }
  return success;
}

void LoadDataReaction::badStackAlert(QList<ImageFileInfo>& summary)
{
  // for (auto it = summary.begin(); it != summary.end(); ++it) {
  foreach (ImageFileInfo image, summary) {
    if (!image.consistent) {
      QMessageBox::warning(
        tomviz::mainWidget(), "Error",
        QString("The dimensions of the images in this stack are inconsistent.\n"
                "This error first occurred at the file:\n\n"
                "%1\n\n"
                "The expected size is (%2, %3), but got (%4, %5) instead.")
          .arg(image.fileName)
          .arg(summary[0].m)
          .arg(summary[0].n)
          .arg(image.m)
          .arg(image.n));
      return;
    }
  }
  return;
}

DataSource* LoadDataReaction::createDataSource(vtkSMProxy* reader,
                                               bool defaultModules, bool child)
{
  // Prompt user for reader configuration, unless it is TIFF.
  QScopedPointer<QDialog> dialog(new pqProxyWidgetDialog(reader));
  bool hasVisibleWidgets =
    qobject_cast<pqProxyWidgetDialog*>(dialog.data())->hasVisibleWidgets();
  if (QString(reader->GetXMLName()) == "TVRawImageReader") {
    dialog.reset(new RAWFileReaderDialog(reader));
    hasVisibleWidgets = true;
    // This will show only a partial filename when reading multiple files as a
    // raw image
    vtkSMPropertyHelper fname(reader, "FilePrefix");
    QFileInfo info(fname.GetAsString());
    dialog->setWindowTitle(QString("Opening %1").arg(info.fileName()));
  } else {
    dialog->setWindowTitle("Configure Reader Parameters");
  }
  dialog->setObjectName("ConfigureReaderDialog");
  if (QString(reader->GetXMLName()) == "TIFFSeriesReader" ||
      hasVisibleWidgets == false || dialog->exec() == QDialog::Accepted) {

    if (!hasData(reader)) {
      qCritical() << "Error: failed to load file!";
      return nullptr;
    }

    auto source = vtkSMSourceProxy::SafeDownCast(reader);
    source->UpdatePipeline();
    auto algo = vtkAlgorithm::SafeDownCast(source->GetClientSideObject());
    auto data = algo->GetOutputDataObject(0);
    auto image = vtkImageData::SafeDownCast(data);

    DataSource* dataSource = new DataSource(image);
    // Do whatever we need to do with a new data source.
    LoadDataReaction::dataSourceAdded(dataSource, defaultModules, child);
    return dataSource;
  }
  return nullptr;
}

void LoadDataReaction::dataSourceAdded(DataSource* dataSource,
                                       bool defaultModules, bool child)
{
  if (!dataSource) {
    return;
  }
  DataSource* previousActiveDataSource =
    ActiveObjects::instance().activeDataSource();
  bool oldMoveObjectsEnabled = ActiveObjects::instance().moveObjectsEnabled();
  ActiveObjects::instance().setMoveObjectsMode(false);
  if (child) {
    ModuleManager::instance().addChildDataSource(dataSource);
  } else {
    auto pipeline = new Pipeline(dataSource);
    PipelineManager::instance().addPipeline(pipeline);
    // TODO Eventually we shouldn't need to keep track of the data sources,
    // the pipeline should do that for us.
    ModuleManager::instance().addDataSource(dataSource);
    if (defaultModules) {
      pipeline->addDefaultModules(dataSource);
    }
  }

  // Work through pathological cases as necessary, prefer active view.
  ActiveObjects::instance().createRenderViewIfNeeded();
  auto view = ActiveObjects::instance().activeView();

  if (!view || QString(view->GetXMLName()) != "RenderView") {
    ActiveObjects::instance().setActiveViewToFirstRenderView();
    view = ActiveObjects::instance().activeView();
  }

  ActiveObjects::instance().setMoveObjectsMode(oldMoveObjectsEnabled);
  if (!previousActiveDataSource) {
    pqRenderView* renderView =
      qobject_cast<pqRenderView*>(pqActiveObjects::instance().activeView());
    if (renderView) {
      tomviz::createCameraOrbit(dataSource->proxy(),
                                renderView->getRenderViewProxy());
    }
  }
}

QJsonObject LoadDataReaction::readerProperties(vtkSMProxy* reader)
{
  QStringList propNames({ "DataScalarType", "DataByteOrder",
                          "NumberOfScalarComponents", "DataExtent" });

  QJsonObject props;
  foreach (QString propName, propNames) {
    auto prop = reader->GetProperty(propName.toLatin1().data());
    if (prop != nullptr) {
      props[propName] = toJson(prop);
    }
  }

  // Special case file name related properties
  auto prop = reader->GetProperty("FileName");
  if (prop != nullptr) {
    props["fileName"] = toJson(prop);
  }
  prop = reader->GetProperty("FileNames");
  if (prop != nullptr) {
    auto fileNames = toJson(prop).toArray();
    if (fileNames.size() > 1) {
      props["fileNames"] = fileNames;
    }
    // Normalize to fileNames for single value.
    else {
      props["fileName"] = fileNames[0];
    }
  }
  prop = reader->GetProperty("FilePrefix");
  if (prop != nullptr) {
    props["fileName"] = toJson(prop);
  }

  return props;
}

void LoadDataReaction::setFileNameProperties(const QJsonObject& props,
                                             vtkSMProxy* reader)
{
  auto prop = reader->GetProperty("FileName");
  if (prop != nullptr) {
    if (!props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' property.";
      return;
    }
    tomviz::setProperty(props["fileName"], prop);
  }
  prop = reader->GetProperty("FileNames");
  if (prop != nullptr) {
    if (!props.contains("fileNames") && !props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' or 'fileNames' property.";
      return;
    }

    if (props.contains("fileNames")) {
      tomviz::setProperty(props["fileNames"], prop);
    } else {
      QJsonArray fileNames;
      fileNames.append(props["fileName"]);
      tomviz::setProperty(fileNames, prop);
    }
  }
  prop = reader->GetProperty("FilePrefix");
  if (prop != nullptr) {
    if (!props.contains("fileName")) {
      qCritical() << "Reader doesn't have 'fileName' property.";
      return;
    }
    tomviz::setProperty(props["fileName"], prop);
  }
}

} // end of namespace tomviz
<|endoftext|>


<commit_before>#include "Iop_Sysclib.h"

using namespace Iop;
using namespace std;

CSysclib::CSysclib(uint8* ram, CStdio& stdio) :
m_ram(ram),
m_stdio(stdio)
{

}

CSysclib::~CSysclib()
{

}

string CSysclib::GetId() const
{
    return "sysclib";
}

string CSysclib::GetFunctionName(unsigned int functionId) const
{
    switch(functionId)
    {
    case 19:
        return "sprintf";
        break;
    case 23:
        return "strcpy";
        break;
    case 27:
        return "strlen";
        break;
    default:
        return "unknown";
        break;
    }
}

void CSysclib::Invoke(CMIPS& context, unsigned int functionId)
{
    switch(functionId)
    {
    case 12:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __memcpy(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            &m_ram[context.m_State.nGPR[CMIPS::A1].nV0],
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 14:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __memset(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            context.m_State.nGPR[CMIPS::A1].nV0,
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 17:
        __memset(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            0,
            context.m_State.nGPR[CMIPS::A1].nV0);
        break;
    case 19:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__sprintf(context));
        break;
    case 22:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strcmp(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0])
            ));
        break;
    case 23:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __strcpy(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0])
            );
        break;
    case 27:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strlen(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0])
            ));
        break;
    case 29:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strncmp(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0));
        break;
    case 30:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __strncpy(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 36:
        assert(context.m_State.nGPR[CMIPS::A1].nV0 == 0);
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strtol(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0
            ));
        break;
    default:
        printf("%s(%0.8X): Unknown function (%d) called.\r\n", __FUNCTION__, context.m_State.nPC, functionId);
        assert(0);
        break;
    }
}

void CSysclib::__memcpy(void* dest, const void* src, unsigned int length)
{
    memcpy(dest, src, length);
}

void CSysclib::__memset(void* dest, int character, unsigned int length)
{
    memset(dest, character, length);
}

uint32 CSysclib::__sprintf(CMIPS& context)
{
    CArgumentIterator args(context);
    char* destination = reinterpret_cast<char*>(&m_ram[args.GetNext()]);
    string output = m_stdio.PrintFormatted(args);
    strcpy(destination, output.c_str());
    return static_cast<uint32>(output.length());
}

uint32 CSysclib::__strlen(const char* string)
{
    return static_cast<uint32>(strlen(string));
}

uint32 CSysclib::__strcmp(const char* s1, const char* s2)
{
    return static_cast<uint32>(strcmp(s1, s2));
}

void CSysclib::__strcpy(char* dst, const char* src)
{
    strcpy(dst, src);
}

uint32 CSysclib::__strncmp(const char* s1, const char* s2, uint32 length)
{
    return static_cast<uint32>(strncmp(s1, s2, length));
}

void CSysclib::__strncpy(char* dst, const char* src, unsigned int count)
{
    strncpy(dst, src, count);
}

uint32 CSysclib::__strtol(const char* string, unsigned int radix)
{
    return strtol(string, NULL, radix);
}
<commit_msg>Added symbol names for implemented functions in Iop_Sysclib.<commit_after>#include "Iop_Sysclib.h"

using namespace Iop;
using namespace std;

CSysclib::CSysclib(uint8* ram, CStdio& stdio) :
m_ram(ram),
m_stdio(stdio)
{

}

CSysclib::~CSysclib()
{

}

string CSysclib::GetId() const
{
    return "sysclib";
}

string CSysclib::GetFunctionName(unsigned int functionId) const
{
    switch(functionId)
    {
	case 12:
		return "memcpy";
		break;
	case 14:
		return "memset";
		break;
    case 19:
        return "sprintf";
        break;
	case 22:
		return "strcmp";
		break;
    case 23:
        return "strcpy";
        break;
    case 27:
        return "strlen";
        break;
	case 36:
		return "strtol";
		break;
    default:
        return "unknown";
        break;
    }
}

void CSysclib::Invoke(CMIPS& context, unsigned int functionId)
{
    switch(functionId)
    {
    case 12:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __memcpy(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            &m_ram[context.m_State.nGPR[CMIPS::A1].nV0],
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 14:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __memset(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            context.m_State.nGPR[CMIPS::A1].nV0,
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 17:
        __memset(
            &m_ram[context.m_State.nGPR[CMIPS::A0].nV0],
            0,
            context.m_State.nGPR[CMIPS::A1].nV0);
        break;
    case 19:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__sprintf(context));
        break;
    case 22:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strcmp(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0])
            ));
        break;
    case 23:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __strcpy(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0])
            );
        break;
    case 27:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strlen(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0])
            ));
        break;
    case 29:
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strncmp(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0));
        break;
    case 30:
        context.m_State.nGPR[CMIPS::V0].nD0 = context.m_State.nGPR[CMIPS::A0].nD0;
        __strncpy(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0);
        break;
    case 36:
        assert(context.m_State.nGPR[CMIPS::A1].nV0 == 0);
        context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(__strtol(
            reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV0]),
            context.m_State.nGPR[CMIPS::A2].nV0
            ));
        break;
    default:
        printf("%s(%0.8X): Unknown function (%d) called.\r\n", __FUNCTION__, context.m_State.nPC, functionId);
        assert(0);
        break;
    }
}

void CSysclib::__memcpy(void* dest, const void* src, unsigned int length)
{
    memcpy(dest, src, length);
}

void CSysclib::__memset(void* dest, int character, unsigned int length)
{
    memset(dest, character, length);
}

uint32 CSysclib::__sprintf(CMIPS& context)
{
    CArgumentIterator args(context);
    char* destination = reinterpret_cast<char*>(&m_ram[args.GetNext()]);
    string output = m_stdio.PrintFormatted(args);
    strcpy(destination, output.c_str());
    return static_cast<uint32>(output.length());
}

uint32 CSysclib::__strlen(const char* string)
{
    return static_cast<uint32>(strlen(string));
}

uint32 CSysclib::__strcmp(const char* s1, const char* s2)
{
    return static_cast<uint32>(strcmp(s1, s2));
}

void CSysclib::__strcpy(char* dst, const char* src)
{
    strcpy(dst, src);
}

uint32 CSysclib::__strncmp(const char* s1, const char* s2, uint32 length)
{
    return static_cast<uint32>(strncmp(s1, s2, length));
}

void CSysclib::__strncpy(char* dst, const char* src, unsigned int count)
{
    strncpy(dst, src, count);
}

uint32 CSysclib::__strtol(const char* string, unsigned int radix)
{
    return strtol(string, NULL, radix);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbUnConstrainedLeastSquareImageFilter.h"
#include "otbISRAUnmixingImageFilter.h"
#include "otbNCLSUnmixingImageFilter.h"
//#include "otbFCLSUnmixingImageFilter.h"
#include "otbMDMDNMFImageFilter.h"

namespace otb
{
namespace Wrapper
{
typedef otb::UnConstrainedLeastSquareImageFilter<DoubleVectorImageType, DoubleVectorImageType, double> UCLSUnmixingFilterType;
typedef otb::ISRAUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             ISRAUnmixingFilterType;
typedef otb::NCLSUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             NCLSUnmixingFilterType;
//typedef otb::FCLSUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             FCLSUnmixingFilterType;
typedef otb::MDMDNMFImageFilter<DoubleVectorImageType, DoubleVectorImageType>                          MDMDNMFUnmixingFilterType;

typedef otb::VectorImageToMatrixImageFilter<DoubleVectorImageType> VectorImageToMatrixImageFilterType;

typedef vnl_vector<double> VectorType;
typedef vnl_matrix<double> MatrixType;

enum DimReductionMethod
{
  DimReductionMethod_NONE,
  DimReductionMethod_PCA,
  DimReductionMethod_MNF
};

enum DimensionalityEstimationMethod
{
  DimensionalityEstimationMethod_ELM
};

enum EndmembersEstimationMethod
{
  EndmembersEstimationMethod_VCA
};

enum UnMixingMethod
{
  UnMixingMethod_UCLS,
  //UnMixingMethod_FCLS,
  UnMixingMethod_NCLS,
  UnMixingMethod_ISRA,
  UnMixingMethod_MDMDNMF,
};

const char* UnMixingMethodNames [] = { "UCLS", "FCLS", "NCLS", "ISRA", "MDMDNMF", };

class HyperspectralUnmixing : public Application
{
public:
  /** Standard class typedefs. */
  typedef HyperspectralUnmixing         Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(HyperspectralUnmixing, otb::Application);

private:
  void DoInit() override
  {
    SetName("HyperspectralUnmixing");
    SetDescription("Estimate abundance maps from an hyperspectral image and a set of endmembers.");

    // Documentation
    SetDocName("Hyperspectral data unmixing");
    SetDocLongDescription("The application applies a linear unmixing algorithm "
    "to an hyperspectral data cube. This method supposes that the mixture between "
    "aterials in the scene is macroscopic and simulates a linear mixing model of "
    "spectra.\n\n"
    "The Linear Mixing Model (LMM) acknowledges that reflectance "
    "spectrum associated with each pixel is a linear combination of pure "
    "materials in the recovery area, commonly known as endmembers. Endmembers can "
    "be estimated using the VertexComponentAnalysis application.\n\n"
    "The application allows estimating the abundance maps with several algorithms:\n\n"
    "* Unconstrained Least Square (ucls)\n"
//  "* Fully Constrained Least Square (fcls)\n"
    "* Image Space Reconstruction Algorithm (isra)\n"
    "* Non-negative constrained\n"
    "* Least Square (ncls)\n"
    "* Minimum Dispersion Constrained Non Negative Matrix Factorization (MDMDNMF)."
    );
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("VertexComponentAnalysis");

    AddDocTag(Tags::Hyperspectral);

    AddParameter(ParameterType_InputImage,  "in",   "Input Image Filename");
    SetParameterDescription("in","The hyperspectral data cube input");

    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out","The output abundance map. The abundance "
      "fraction are stored in a multispectral image where band N corresponds "
      "to the fraction of endmembers N in each pixel.");

    AddParameter(ParameterType_InputImage,  "ie",   "Input endmembers");
    SetParameterDescription("ie","The endmembers (estimated pure pixels) to "
      "use for unmixing. Must be stored as a multispectral image, where "
      "each pixel is interpreted as an endmember.");

    AddParameter(ParameterType_Choice, "ua", "Unmixing algorithm");
    SetParameterDescription("ua", "The algorithm to use for unmixing");
    MandatoryOff("ua");
    AddChoice("ua.ucls", "UCLS");
    SetParameterDescription("ua.ucls", "Unconstrained Least Square");

//    AddChoice("ua.fcls", "FCLS");
//    SetParameterDescription("ua.fcls", "Fully constrained Least Square");

    AddChoice("ua.ncls", "NCLS");
    SetParameterDescription("ua.ncls", "Non-negative constrained Least Square");

    AddChoice("ua.isra", "ISRA");
    SetParameterDescription("ua.isra", "Image Space Reconstruction Algorithm");

    AddChoice("ua.mdmdnmf", "MDMDNMF");
    SetParameterDescription("ua.mdmdnmf", "Minimum Dispersion Constrained Non Negative Matrix Factorization");
    SetParameterString("ua", "ucls");
    // Doc example parameter settings
    SetDocExampleParameterValue("in", "cupriteSubHsi.tif");
    SetDocExampleParameterValue("ie", "cupriteEndmembers.tif");
    SetDocExampleParameterValue("out", "HyperspectralUnmixing.tif double");
    SetDocExampleParameterValue("ua", "ucls");

    SetOfficialDocLink();
  }

  void DoUpdateParameters() override
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute() override
  {
    m_ProcessObjects.clear();

    DoubleVectorImageType::Pointer inputImage = GetParameterDoubleVectorImage("in");
    DoubleVectorImageType::Pointer endmembersImage = GetParameterDoubleVectorImage("ie");

    /*
     * Transform Endmembers image to matrix representation
     */
    otbAppLogINFO("Endmembers extracted");
    otbAppLogINFO("Converting endmembers to matrix");
    VectorImageToMatrixImageFilterType::Pointer endMember2Matrix = VectorImageToMatrixImageFilterType::New();
    endMember2Matrix->SetInput(endmembersImage);
    endMember2Matrix->Update();

    MatrixType endMembersMatrix = endMember2Matrix->GetMatrix();
    otbAppLogINFO("Endmembers matrix : " << endMembersMatrix);

    /*
     * Unmix
     */
    DoubleVectorImageType::Pointer abundanceMap;

    switch ( static_cast<UnMixingMethod>(GetParameterInt("ua")) )
    {
    case UnMixingMethod_UCLS:
      {
      otbAppLogINFO("UCLS Unmixing");

      UCLSUnmixingFilterType::Pointer unmixer =
          UCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetMatrix(endMembersMatrix);
      unmixer->SetNumberOfThreads(1); // FIXME : currently buggy

      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    case UnMixingMethod_ISRA:
      {
      otbAppLogINFO("ISRA Unmixing");

      ISRAUnmixingFilterType::Pointer unmixer =
          ISRAUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    case UnMixingMethod_NCLS:
      {
      otbAppLogINFO("NCLS Unmixing");

      NCLSUnmixingFilterType::Pointer unmixer =
          NCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
      /*
    case UnMixingMethod_FCLS:
      {
      otbAppLogINFO("FCLS Unmixing");

      FCLSUnmixingFilterType::Pointer unmixer =
          FCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
      */
    case UnMixingMethod_MDMDNMF:
      {
      otbAppLogINFO("MDMD-NMF Unmixing");

      MDMDNMFUnmixingFilterType::Pointer unmixer =
          MDMDNMFUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    default:
      break;
    }

    SetParameterOutputImage<DoubleVectorImageType>("out", abundanceMap);

  }

  std::vector<itk::ProcessObject::Pointer> m_ProcessObjects;
};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::HyperspectralUnmixing)
<commit_msg>ENH: endmember matrix is now only printed in Debug log level<commit_after>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbUnConstrainedLeastSquareImageFilter.h"
#include "otbISRAUnmixingImageFilter.h"
#include "otbNCLSUnmixingImageFilter.h"
//#include "otbFCLSUnmixingImageFilter.h"
#include "otbMDMDNMFImageFilter.h"

namespace otb
{
namespace Wrapper
{
typedef otb::UnConstrainedLeastSquareImageFilter<DoubleVectorImageType, DoubleVectorImageType, double> UCLSUnmixingFilterType;
typedef otb::ISRAUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             ISRAUnmixingFilterType;
typedef otb::NCLSUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             NCLSUnmixingFilterType;
//typedef otb::FCLSUnmixingImageFilter<DoubleVectorImageType, DoubleVectorImageType, double>             FCLSUnmixingFilterType;
typedef otb::MDMDNMFImageFilter<DoubleVectorImageType, DoubleVectorImageType>                          MDMDNMFUnmixingFilterType;

typedef otb::VectorImageToMatrixImageFilter<DoubleVectorImageType> VectorImageToMatrixImageFilterType;

typedef vnl_vector<double> VectorType;
typedef vnl_matrix<double> MatrixType;

enum DimReductionMethod
{
  DimReductionMethod_NONE,
  DimReductionMethod_PCA,
  DimReductionMethod_MNF
};

enum DimensionalityEstimationMethod
{
  DimensionalityEstimationMethod_ELM
};

enum EndmembersEstimationMethod
{
  EndmembersEstimationMethod_VCA
};

enum UnMixingMethod
{
  UnMixingMethod_UCLS,
  //UnMixingMethod_FCLS,
  UnMixingMethod_NCLS,
  UnMixingMethod_ISRA,
  UnMixingMethod_MDMDNMF,
};

const char* UnMixingMethodNames [] = { "UCLS", "FCLS", "NCLS", "ISRA", "MDMDNMF", };

class HyperspectralUnmixing : public Application
{
public:
  /** Standard class typedefs. */
  typedef HyperspectralUnmixing         Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(HyperspectralUnmixing, otb::Application);

private:
  void DoInit() override
  {
    SetName("HyperspectralUnmixing");
    SetDescription("Estimate abundance maps from an hyperspectral image and a set of endmembers.");

    // Documentation
    SetDocName("Hyperspectral data unmixing");
    SetDocLongDescription("The application applies a linear unmixing algorithm "
    "to an hyperspectral data cube. This method supposes that the mixture between "
    "aterials in the scene is macroscopic and simulates a linear mixing model of "
    "spectra.\n\n"
    "The Linear Mixing Model (LMM) acknowledges that reflectance "
    "spectrum associated with each pixel is a linear combination of pure "
    "materials in the recovery area, commonly known as endmembers. Endmembers can "
    "be estimated using the VertexComponentAnalysis application.\n\n"
    "The application allows estimating the abundance maps with several algorithms:\n\n"
    "* Unconstrained Least Square (ucls)\n"
//  "* Fully Constrained Least Square (fcls)\n"
    "* Image Space Reconstruction Algorithm (isra)\n"
    "* Non-negative constrained\n"
    "* Least Square (ncls)\n"
    "* Minimum Dispersion Constrained Non Negative Matrix Factorization (MDMDNMF)."
    );
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("VertexComponentAnalysis");

    AddDocTag(Tags::Hyperspectral);

    AddParameter(ParameterType_InputImage,  "in",   "Input Image Filename");
    SetParameterDescription("in","The hyperspectral data cube input");

    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out","The output abundance map. The abundance "
      "fraction are stored in a multispectral image where band N corresponds "
      "to the fraction of endmembers N in each pixel.");

    AddParameter(ParameterType_InputImage,  "ie",   "Input endmembers");
    SetParameterDescription("ie","The endmembers (estimated pure pixels) to "
      "use for unmixing. Must be stored as a multispectral image, where "
      "each pixel is interpreted as an endmember.");

    AddParameter(ParameterType_Choice, "ua", "Unmixing algorithm");
    SetParameterDescription("ua", "The algorithm to use for unmixing");
    MandatoryOff("ua");
    AddChoice("ua.ucls", "UCLS");
    SetParameterDescription("ua.ucls", "Unconstrained Least Square");

//    AddChoice("ua.fcls", "FCLS");
//    SetParameterDescription("ua.fcls", "Fully constrained Least Square");

    AddChoice("ua.ncls", "NCLS");
    SetParameterDescription("ua.ncls", "Non-negative constrained Least Square");

    AddChoice("ua.isra", "ISRA");
    SetParameterDescription("ua.isra", "Image Space Reconstruction Algorithm");

    AddChoice("ua.mdmdnmf", "MDMDNMF");
    SetParameterDescription("ua.mdmdnmf", "Minimum Dispersion Constrained Non Negative Matrix Factorization");
    SetParameterString("ua", "ucls");
    // Doc example parameter settings
    SetDocExampleParameterValue("in", "cupriteSubHsi.tif");
    SetDocExampleParameterValue("ie", "cupriteEndmembers.tif");
    SetDocExampleParameterValue("out", "HyperspectralUnmixing.tif double");
    SetDocExampleParameterValue("ua", "ucls");

    SetOfficialDocLink();
  }

  void DoUpdateParameters() override
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute() override
  {
    m_ProcessObjects.clear();

    DoubleVectorImageType::Pointer inputImage = GetParameterDoubleVectorImage("in");
    DoubleVectorImageType::Pointer endmembersImage = GetParameterDoubleVectorImage("ie");

    /*
     * Transform Endmembers image to matrix representation
     */
    otbAppLogINFO("Endmembers extracted");
    otbAppLogINFO("Converting endmembers to matrix");
    VectorImageToMatrixImageFilterType::Pointer endMember2Matrix = VectorImageToMatrixImageFilterType::New();
    endMember2Matrix->SetInput(endmembersImage);
    endMember2Matrix->Update();

    MatrixType endMembersMatrix = endMember2Matrix->GetMatrix();
    otbAppLogDEBUG("Endmembers matrix : " << endMembersMatrix);

    /*
     * Unmix
     */
    DoubleVectorImageType::Pointer abundanceMap;

    switch ( static_cast<UnMixingMethod>(GetParameterInt("ua")) )
    {
    case UnMixingMethod_UCLS:
      {
      otbAppLogINFO("UCLS Unmixing");

      UCLSUnmixingFilterType::Pointer unmixer =
          UCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetMatrix(endMembersMatrix);
      unmixer->SetNumberOfThreads(1); // FIXME : currently buggy

      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    case UnMixingMethod_ISRA:
      {
      otbAppLogINFO("ISRA Unmixing");

      ISRAUnmixingFilterType::Pointer unmixer =
          ISRAUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    case UnMixingMethod_NCLS:
      {
      otbAppLogINFO("NCLS Unmixing");

      NCLSUnmixingFilterType::Pointer unmixer =
          NCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
      /*
    case UnMixingMethod_FCLS:
      {
      otbAppLogINFO("FCLS Unmixing");

      FCLSUnmixingFilterType::Pointer unmixer =
          FCLSUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
      */
    case UnMixingMethod_MDMDNMF:
      {
      otbAppLogINFO("MDMD-NMF Unmixing");

      MDMDNMFUnmixingFilterType::Pointer unmixer =
          MDMDNMFUnmixingFilterType::New();

      unmixer->SetInput(inputImage);
      unmixer->SetEndmembersMatrix(endMembersMatrix);
      abundanceMap = unmixer->GetOutput();
      m_ProcessObjects.push_back(unmixer.GetPointer());

      }
      break;
    default:
      break;
    }

    SetParameterOutputImage<DoubleVectorImageType>("out", abundanceMap);

  }

  std::vector<itk::ProcessObject::Pointer> m_ProcessObjects;
};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::HyperspectralUnmixing)
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkSigmoidTransferFunction_hxx
#define __itkSigmoidTransferFunction_hxx

#include "itkSigmoidTransferFunction.h"

namespace itk
{
namespace Statistics
{

/** Constructor */
template<typename ScalarType>
SigmoidTransferFunction< ScalarType>
::SigmoidTransferFunction()
{
  m_Alpha = 1.0;
  m_Beta = 0.0;
  m_OutputMinimum = NumericTraits<ScalarType>::min();
  m_OutputMaximum = NumericTraits<ScalarType>::max();
}

/** Destructor */
template<typename ScalarType>
SigmoidTransferFunction<ScalarType>
::~SigmoidTransferFunction()
{
}

/** Evaluate */
template<typename ScalarType>
ScalarType
SigmoidTransferFunction<ScalarType>
::Evaluate(const ScalarType& input)  const
{
  const ScalarType x = (static_cast<ScalarType>(input) - m_Beta) / m_Alpha;
  const ScalarType e = 1.0 / (1.0 + vcl_exp( static_cast< typename NumericTraits< ScalarType >::RealType >(-x)));
  const ScalarType v = (m_OutputMaximum - m_OutputMinimum) * e
                     + m_OutputMinimum;
  return v;
}

/** Evaluate Derivatives */
template<typename ScalarType>
ScalarType
SigmoidTransferFunction< ScalarType>
::EvaluateDerivative(const ScalarType& input)  const
{
  ScalarType f = Evaluate(input);
  return f * (1 - f);
}

/** Print the object */
template<typename ScalarType>
void
SigmoidTransferFunction<ScalarType>
::PrintSelf( std::ostream& os, Indent indent ) const
{
  os << indent << "SigmoidTransferFunction(" << this << ")" << std::endl;
  os << indent << "m_Alpha = " << m_Alpha << std::endl;
  os << indent << "m_Beta = " << m_Beta << std::endl;
  os << indent << "m_OutputMinimum = " << m_OutputMinimum << std::endl;
  os << indent << "m_OutputMaximum = " << m_OutputMaximum << std::endl;
  Superclass::PrintSelf( os, indent );
}

} // end namespace Statistics
} // end namespace itk

#endif
<commit_msg>COMP: SigmoidTransferFunction conversion from double.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkSigmoidTransferFunction_hxx
#define __itkSigmoidTransferFunction_hxx

#include "itkSigmoidTransferFunction.h"

namespace itk
{
namespace Statistics
{

/** Constructor */
template<typename ScalarType>
SigmoidTransferFunction< ScalarType>
::SigmoidTransferFunction()
{
  m_Alpha = 1.0;
  m_Beta = 0.0;
  m_OutputMinimum = NumericTraits<ScalarType>::min();
  m_OutputMaximum = NumericTraits<ScalarType>::max();
}

/** Destructor */
template<typename ScalarType>
SigmoidTransferFunction<ScalarType>
::~SigmoidTransferFunction()
{
}

/** Evaluate */
template<typename ScalarType>
ScalarType
SigmoidTransferFunction<ScalarType>
::Evaluate(const ScalarType& input)  const
{
  typedef typename NumericTraits< ScalarType >::RealType RealType;
  const RealType x = static_cast< RealType >( input - m_Beta ) / m_Alpha;
  const RealType e = 1.0 / (1.0 + vcl_exp( static_cast< typename NumericTraits< ScalarType >::RealType >(-x)));
  const ScalarType v = static_cast< ScalarType >( (m_OutputMaximum - m_OutputMinimum) * e )
                     + m_OutputMinimum;
  return v;
}

/** Evaluate Derivatives */
template<typename ScalarType>
ScalarType
SigmoidTransferFunction< ScalarType>
::EvaluateDerivative(const ScalarType& input)  const
{
  ScalarType f = Evaluate(input);
  return f * (1 - f);
}

/** Print the object */
template<typename ScalarType>
void
SigmoidTransferFunction<ScalarType>
::PrintSelf( std::ostream& os, Indent indent ) const
{
  os << indent << "SigmoidTransferFunction(" << this << ")" << std::endl;
  os << indent << "m_Alpha = " << m_Alpha << std::endl;
  os << indent << "m_Beta = " << m_Beta << std::endl;
  os << indent << "m_OutputMinimum = " << m_OutputMinimum << std::endl;
  os << indent << "m_OutputMaximum = " << m_OutputMaximum << std::endl;
  Superclass::PrintSelf( os, indent );
}

} // end namespace Statistics
} // end namespace itk

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of Google Inc. nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/dom/CustomElementRegistrationContext.h"

#include "HTMLNames.h"
#include "MathMLNames.h"
#include "SVGNames.h"
#include "core/dom/CustomElementCallbackDispatcher.h"
#include "core/dom/CustomElementDefinition.h"
#include "core/dom/CustomElementRegistry.h"
#include "core/dom/CustomElementUpgradeCandidateMap.h"
#include "core/dom/Element.h"
#include "core/html/HTMLElement.h"
#include "core/html/HTMLUnknownElement.h"
#include "core/svg/SVGElement.h"

namespace WebCore {

// The null registration context is used by documents that do not
// process custom elements. All of its operations are no-ops.
class NullRegistrationContext : public CustomElementRegistrationContext {
public:
    NullRegistrationContext() { }
    virtual ~NullRegistrationContext() { }

    virtual void registerElement(Document*, CustomElementConstructorBuilder*, const AtomicString&, ExceptionCode& ec) OVERRIDE { ec = NotSupportedError; }

    virtual PassRefPtr<Element> createCustomTagElement(Document*, const QualifiedName&) OVERRIDE;
    virtual void didGiveTypeExtension(Element*) OVERRIDE { }

    virtual void customElementAttributeDidChange(Element*, const AtomicString&, const AtomicString&, const AtomicString&) OVERRIDE { }
    virtual void customElementDidEnterDocument(Element*) OVERRIDE { }
    virtual void customElementDidLeaveDocument(Element*) OVERRIDE { }
};

PassRefPtr<Element> NullRegistrationContext::createCustomTagElement(Document* document, const QualifiedName& tagName)
{
    ASSERT(isCustomTagName(tagName.localName()));

    if (!document)
        return 0;

    if (HTMLNames::xhtmlNamespaceURI == tagName.namespaceURI())
        return HTMLUnknownElement::create(tagName, document);
    if (SVGNames::svgNamespaceURI == tagName.namespaceURI())
        return SVGElement::create(tagName, document);
    return Element::create(tagName, document);
}

PassRefPtr<CustomElementRegistrationContext> CustomElementRegistrationContext::nullRegistrationContext()
{
    DEFINE_STATIC_LOCAL(RefPtr<NullRegistrationContext>, instance, ());
    if (!instance)
        instance = adoptRef(new NullRegistrationContext());
    return instance.get();
}

// An active registration context is used by documents that are
// processing custom elements.
class ActiveRegistrationContext : public CustomElementRegistrationContext {
public:
    ActiveRegistrationContext() { }
    virtual ~ActiveRegistrationContext() { }

    virtual void registerElement(Document*, CustomElementConstructorBuilder*, const AtomicString& type, ExceptionCode&) OVERRIDE;

    virtual PassRefPtr<Element> createCustomTagElement(Document*, const QualifiedName&) OVERRIDE;
    virtual void didGiveTypeExtension(Element*) OVERRIDE;

    virtual void customElementAttributeDidChange(Element*, const AtomicString&, const AtomicString&, const AtomicString&) OVERRIDE;
    virtual void customElementDidEnterDocument(Element*) OVERRIDE;
    virtual void customElementDidLeaveDocument(Element*) OVERRIDE;
    virtual void customElementIsBeingDestroyed(Element*) OVERRIDE;

private:
    CustomElementDefinition* definitionFor(Element*) const;

    void didCreateUnresolvedElement(const CustomElementDescriptor&, Element*);
    void didResolveElement(CustomElementDefinition*, Element*) const;

    CustomElementRegistry m_registry;

    // Element creation
    CustomElementUpgradeCandidateMap m_candidates;
};

void ActiveRegistrationContext::registerElement(Document* document, CustomElementConstructorBuilder* constructorBuilder, const AtomicString& type, ExceptionCode& ec)
{
    CustomElementDefinition* definition = m_registry.registerElement(document, constructorBuilder, type, ec);

    if (!definition)
        return;

    // Upgrade elements that were waiting for this definition.
    const CustomElementUpgradeCandidateMap::ElementSet& upgradeCandidates = m_candidates.takeUpgradeCandidatesFor(definition->descriptor());
    for (CustomElementUpgradeCandidateMap::ElementSet::const_iterator it = upgradeCandidates.begin(); it != upgradeCandidates.end(); ++it)
        didResolveElement(definition, *it);
}

CustomElementDefinition* ActiveRegistrationContext::definitionFor(Element* element) const
{
    ASSERT(element->document()->registrationContext() == this);
    const CustomElementDescriptor& descriptor = describe(element);
    return m_registry.find(descriptor);
}

PassRefPtr<Element> ActiveRegistrationContext::createCustomTagElement(Document* document, const QualifiedName& tagName)
{
    ASSERT(isCustomTagName(tagName.localName()));

    if (!document)
        return 0;

    RefPtr<Element> element;

    if (HTMLNames::xhtmlNamespaceURI == tagName.namespaceURI()) {
        element = HTMLElement::create(tagName, document);
    } else if (SVGNames::svgNamespaceURI == tagName.namespaceURI()) {
        element = SVGElement::create(tagName, document);
    } else {
        // XML elements are not custom elements, so return early.
        return Element::create(tagName, document);
    }

    element->setIsCustomElement();

    const CustomElementDescriptor& descriptor = describe(element.get());
    CustomElementDefinition* definition = m_registry.find(descriptor);
    if (definition)
        didResolveElement(definition, element.get());
    else
        didCreateUnresolvedElement(descriptor, element.get());

    return element.release();
}

void ActiveRegistrationContext::didCreateUnresolvedElement(const CustomElementDescriptor& descriptor, Element* element)
{
    m_candidates.add(descriptor, element);
}

void ActiveRegistrationContext::didResolveElement(CustomElementDefinition* definition, Element* element) const
{
    CustomElementCallbackDispatcher::instance().enqueueCreatedCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::didGiveTypeExtension(Element* element)
{
    const CustomElementDescriptor& descriptor = describe(element);
    CustomElementDefinition* definition = m_registry.find(descriptor);
    if (definition)
        didResolveElement(definition, element);
    else
        didCreateUnresolvedElement(descriptor, element);
}

void ActiveRegistrationContext::customElementAttributeDidChange(Element* element, const AtomicString& name, const AtomicString& oldValue, const AtomicString& newValue)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueAttributeChangedCallback(definition->callbacks(), element, name, oldValue, newValue);
}

void ActiveRegistrationContext::customElementDidEnterDocument(Element* element)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueEnteredDocumentCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::customElementDidLeaveDocument(Element* element)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueLeftDocumentCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::customElementIsBeingDestroyed(Element* element)
{
    CustomElementRegistrationContext::customElementIsBeingDestroyed(element);
    m_candidates.remove(element);
}

PassRefPtr<CustomElementRegistrationContext> CustomElementRegistrationContext::create()
{
    return adoptRef(new ActiveRegistrationContext());
}

bool CustomElementRegistrationContext::isValidTypeName(const AtomicString& name)
{
    if (notFound == name.find('-'))
        return false;

    DEFINE_STATIC_LOCAL(Vector<AtomicString>, reservedNames, ());
    if (reservedNames.isEmpty()) {
        reservedNames.append(MathMLNames::annotation_xmlTag.localName());
        reservedNames.append(SVGNames::color_profileTag.localName());
        reservedNames.append(SVGNames::font_faceTag.localName());
        reservedNames.append(SVGNames::font_face_srcTag.localName());
        reservedNames.append(SVGNames::font_face_uriTag.localName());
        reservedNames.append(SVGNames::font_face_formatTag.localName());
        reservedNames.append(SVGNames::font_face_nameTag.localName());
        reservedNames.append(SVGNames::missing_glyphTag.localName());
    }

    if (notFound != reservedNames.find(name))
        return false;

    return Document::isValidName(name.string());
}

bool CustomElementRegistrationContext::isCustomTagName(const AtomicString& localName)
{
    return isValidTypeName(localName);
}

CustomElementDescriptor CustomElementRegistrationContext::describe(Element* element)
{
    ASSERT(element->isCustomElement());

    // If an element has a custom tag name it takes precedence over
    // the "is" attribute (if any).
    const AtomicString& type = isCustomTagName(element->localName())
        ? element->localName()
        : typeExtension(element);

    return CustomElementDescriptor(type, element->namespaceURI(), element->localName());
}

void CustomElementRegistrationContext::setIsAttributeAndTypeExtension(Element* element, const AtomicString& type)
{
    ASSERT(element);
    ASSERT(!type.isEmpty());
    element->setAttribute(HTMLNames::isAttr, type);
    setTypeExtension(element, type);
}

void CustomElementRegistrationContext::setTypeExtension(Element* element, const AtomicString& type)
{
    if (!element->isHTMLElement() && !element->isSVGElement())
        return;

    if (isCustomTagName(element->localName()))
        return; // custom tags take precedence over type extensions

    TypeExtensionMap::AddResult result = typeExtensionMap()->add(element, type);
    ASSERT(result.isNewEntry); // Type extensions should only be set once
    element->setIsCustomElement();
    element->document()->registrationContext()->didGiveTypeExtension(element);
}

void CustomElementRegistrationContext::customElementIsBeingDestroyed(Element* element)
{
    ASSERT(element->isCustomElement());
    typeExtensionMap()->remove(element);
}

const AtomicString& CustomElementRegistrationContext::typeExtension(Element* element)
{
    TypeExtensionMap::const_iterator it = typeExtensionMap()->find(element);
    ASSERT(it != typeExtensionMap()->end());
    return it->value;
}

CustomElementRegistrationContext::TypeExtensionMap* CustomElementRegistrationContext::typeExtensionMap()
{
    DEFINE_STATIC_LOCAL(TypeExtensionMap, typeExtensionMap, ());
    return &typeExtensionMap;
}

} // namespace WebCore
<commit_msg>Remove and eliminate redundant, duplicative Custom Element resolution code.<commit_after>/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of Google Inc. nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/dom/CustomElementRegistrationContext.h"

#include "HTMLNames.h"
#include "MathMLNames.h"
#include "SVGNames.h"
#include "core/dom/CustomElementCallbackDispatcher.h"
#include "core/dom/CustomElementDefinition.h"
#include "core/dom/CustomElementRegistry.h"
#include "core/dom/CustomElementUpgradeCandidateMap.h"
#include "core/dom/Element.h"
#include "core/html/HTMLElement.h"
#include "core/html/HTMLUnknownElement.h"
#include "core/svg/SVGElement.h"

namespace WebCore {

// The null registration context is used by documents that do not
// process custom elements. All of its operations are no-ops.
class NullRegistrationContext : public CustomElementRegistrationContext {
public:
    NullRegistrationContext() { }
    virtual ~NullRegistrationContext() { }

    virtual void registerElement(Document*, CustomElementConstructorBuilder*, const AtomicString&, ExceptionCode& ec) OVERRIDE { ec = NotSupportedError; }

    virtual PassRefPtr<Element> createCustomTagElement(Document*, const QualifiedName&) OVERRIDE;
    virtual void didGiveTypeExtension(Element*) OVERRIDE { }

    virtual void customElementAttributeDidChange(Element*, const AtomicString&, const AtomicString&, const AtomicString&) OVERRIDE { }
    virtual void customElementDidEnterDocument(Element*) OVERRIDE { }
    virtual void customElementDidLeaveDocument(Element*) OVERRIDE { }
};

PassRefPtr<Element> NullRegistrationContext::createCustomTagElement(Document* document, const QualifiedName& tagName)
{
    ASSERT(isCustomTagName(tagName.localName()));

    if (!document)
        return 0;

    if (HTMLNames::xhtmlNamespaceURI == tagName.namespaceURI())
        return HTMLUnknownElement::create(tagName, document);
    if (SVGNames::svgNamespaceURI == tagName.namespaceURI())
        return SVGElement::create(tagName, document);
    return Element::create(tagName, document);
}

PassRefPtr<CustomElementRegistrationContext> CustomElementRegistrationContext::nullRegistrationContext()
{
    DEFINE_STATIC_LOCAL(RefPtr<NullRegistrationContext>, instance, ());
    if (!instance)
        instance = adoptRef(new NullRegistrationContext());
    return instance.get();
}

// An active registration context is used by documents that are
// processing custom elements.
class ActiveRegistrationContext : public CustomElementRegistrationContext {
public:
    ActiveRegistrationContext() { }
    virtual ~ActiveRegistrationContext() { }

    virtual void registerElement(Document*, CustomElementConstructorBuilder*, const AtomicString& type, ExceptionCode&) OVERRIDE;

    virtual PassRefPtr<Element> createCustomTagElement(Document*, const QualifiedName&) OVERRIDE;
    virtual void didGiveTypeExtension(Element*) OVERRIDE;

    virtual void customElementAttributeDidChange(Element*, const AtomicString&, const AtomicString&, const AtomicString&) OVERRIDE;
    virtual void customElementDidEnterDocument(Element*) OVERRIDE;
    virtual void customElementDidLeaveDocument(Element*) OVERRIDE;
    virtual void customElementIsBeingDestroyed(Element*) OVERRIDE;

private:
    void resolve(Element*);
    void didResolveElement(CustomElementDefinition*, Element*);
    void didCreateUnresolvedElement(const CustomElementDescriptor&, Element*);

    CustomElementDefinition* definitionFor(Element*) const;

    CustomElementRegistry m_registry;

    // Element creation
    CustomElementUpgradeCandidateMap m_candidates;
};

void ActiveRegistrationContext::registerElement(Document* document, CustomElementConstructorBuilder* constructorBuilder, const AtomicString& type, ExceptionCode& ec)
{
    CustomElementDefinition* definition = m_registry.registerElement(document, constructorBuilder, type, ec);

    if (!definition)
        return;

    // Upgrade elements that were waiting for this definition.
    const CustomElementUpgradeCandidateMap::ElementSet& upgradeCandidates = m_candidates.takeUpgradeCandidatesFor(definition->descriptor());
    for (CustomElementUpgradeCandidateMap::ElementSet::const_iterator it = upgradeCandidates.begin(); it != upgradeCandidates.end(); ++it)
        didResolveElement(definition, *it);
}

PassRefPtr<Element> ActiveRegistrationContext::createCustomTagElement(Document* document, const QualifiedName& tagName)
{
    ASSERT(isCustomTagName(tagName.localName()));

    if (!document)
        return 0;

    RefPtr<Element> element;

    if (HTMLNames::xhtmlNamespaceURI == tagName.namespaceURI()) {
        element = HTMLElement::create(tagName, document);
    } else if (SVGNames::svgNamespaceURI == tagName.namespaceURI()) {
        element = SVGElement::create(tagName, document);
    } else {
        // XML elements are not custom elements, so return early.
        return Element::create(tagName, document);
    }

    element->setIsCustomElement();
    resolve(element.get());
    return element.release();
}

void ActiveRegistrationContext::didGiveTypeExtension(Element* element)
{
    resolve(element);
}

void ActiveRegistrationContext::resolve(Element* element)
{
    ASSERT(element->isCustomElement());
    ASSERT(!element->isUpgradedCustomElement());
    const CustomElementDescriptor& descriptor = describe(element);
    CustomElementDefinition* definition = m_registry.find(descriptor);
    if (definition)
        didResolveElement(definition, element);
    else
        didCreateUnresolvedElement(descriptor, element);
}

void ActiveRegistrationContext::didResolveElement(CustomElementDefinition* definition, Element* element)
{
    CustomElementCallbackDispatcher::instance().enqueueCreatedCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::didCreateUnresolvedElement(const CustomElementDescriptor& descriptor, Element* element)
{
    m_candidates.add(descriptor, element);
}

CustomElementDefinition* ActiveRegistrationContext::definitionFor(Element* element) const
{
    ASSERT(element->document()->registrationContext() == this);
    const CustomElementDescriptor& descriptor = describe(element);
    return m_registry.find(descriptor);
}

void ActiveRegistrationContext::customElementAttributeDidChange(Element* element, const AtomicString& name, const AtomicString& oldValue, const AtomicString& newValue)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueAttributeChangedCallback(definition->callbacks(), element, name, oldValue, newValue);
}

void ActiveRegistrationContext::customElementDidEnterDocument(Element* element)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueEnteredDocumentCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::customElementDidLeaveDocument(Element* element)
{
    ASSERT(element->isUpgradedCustomElement());
    CustomElementDefinition* definition = definitionFor(element);
    CustomElementCallbackDispatcher::instance().enqueueLeftDocumentCallback(definition->callbacks(), element);
}

void ActiveRegistrationContext::customElementIsBeingDestroyed(Element* element)
{
    CustomElementRegistrationContext::customElementIsBeingDestroyed(element);
    m_candidates.remove(element);
}

PassRefPtr<CustomElementRegistrationContext> CustomElementRegistrationContext::create()
{
    return adoptRef(new ActiveRegistrationContext());
}

bool CustomElementRegistrationContext::isValidTypeName(const AtomicString& name)
{
    if (notFound == name.find('-'))
        return false;

    DEFINE_STATIC_LOCAL(Vector<AtomicString>, reservedNames, ());
    if (reservedNames.isEmpty()) {
        reservedNames.append(MathMLNames::annotation_xmlTag.localName());
        reservedNames.append(SVGNames::color_profileTag.localName());
        reservedNames.append(SVGNames::font_faceTag.localName());
        reservedNames.append(SVGNames::font_face_srcTag.localName());
        reservedNames.append(SVGNames::font_face_uriTag.localName());
        reservedNames.append(SVGNames::font_face_formatTag.localName());
        reservedNames.append(SVGNames::font_face_nameTag.localName());
        reservedNames.append(SVGNames::missing_glyphTag.localName());
    }

    if (notFound != reservedNames.find(name))
        return false;

    return Document::isValidName(name.string());
}

bool CustomElementRegistrationContext::isCustomTagName(const AtomicString& localName)
{
    return isValidTypeName(localName);
}

CustomElementDescriptor CustomElementRegistrationContext::describe(Element* element)
{
    ASSERT(element->isCustomElement());

    // If an element has a custom tag name it takes precedence over
    // the "is" attribute (if any).
    const AtomicString& type = isCustomTagName(element->localName())
        ? element->localName()
        : typeExtension(element);

    return CustomElementDescriptor(type, element->namespaceURI(), element->localName());
}

void CustomElementRegistrationContext::setIsAttributeAndTypeExtension(Element* element, const AtomicString& type)
{
    ASSERT(element);
    ASSERT(!type.isEmpty());
    element->setAttribute(HTMLNames::isAttr, type);
    setTypeExtension(element, type);
}

void CustomElementRegistrationContext::setTypeExtension(Element* element, const AtomicString& type)
{
    if (!element->isHTMLElement() && !element->isSVGElement())
        return;

    if (isCustomTagName(element->localName()))
        return; // custom tags take precedence over type extensions

    TypeExtensionMap::AddResult result = typeExtensionMap()->add(element, type);
    ASSERT(result.isNewEntry); // Type extensions should only be set once
    element->setIsCustomElement();
    element->document()->registrationContext()->didGiveTypeExtension(element);
}

void CustomElementRegistrationContext::customElementIsBeingDestroyed(Element* element)
{
    ASSERT(element->isCustomElement());
    typeExtensionMap()->remove(element);
}

const AtomicString& CustomElementRegistrationContext::typeExtension(Element* element)
{
    TypeExtensionMap::const_iterator it = typeExtensionMap()->find(element);
    ASSERT(it != typeExtensionMap()->end());
    return it->value;
}

CustomElementRegistrationContext::TypeExtensionMap* CustomElementRegistrationContext::typeExtensionMap()
{
    DEFINE_STATIC_LOCAL(TypeExtensionMap, typeExtensionMap, ());
    return &typeExtensionMap;
}

} // namespace WebCore
<|endoftext|>