Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/badges/code_issues.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 44 additions & 2 deletions code/+nansen/+config/+project/@Project/Project.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
end

properties (SetAccess = private)
FolderPath char % Path to the project folder
FolderPath char % Path to the project folder
FileAdapterRegistry nansen.plugin.fileadapter.Registry % Registry for FileAdapter plugins
end

properties (SetAccess = private, Hidden)
Expand Down Expand Up @@ -89,6 +90,7 @@

obj.FolderPath = projectFolder;
obj.initializeModules()
obj.initializeFileAdapterRegistry()
end
end

Expand Down Expand Up @@ -666,8 +668,9 @@ function updateModules(obj)

% Remove any modules that were included but not any more
for i = numel(removeIdx):-1:1
% removedModule = obj.IncludedModules(removeIdx(i));
removedId = string(currentModules(removeIdx(i)).ID);
obj.IncludedModules(removeIdx(i)) = [];
obj.FileAdapterRegistry.removeSource(removedId);

% Remove variables (Not needed?):
%variableList = removedModule.DataVariables;
Expand All @@ -682,10 +685,49 @@ function updateModules(obj)
% Update variable model based on module's template variables
variableList = table2struct( module.getTable('DataVariables') );
obj.VariableModel.addDataVariableSet(variableList)

moduleRoot = fileparts(module.getFileAdapterFolder());
obj.FileAdapterRegistry.addSource(string(module.ID), moduleRoot);
end

if ~isempty(addedModuleID) || ~isempty(removeIdx)
obj.FileAdapterRegistry.reconcile();
notify(obj, 'ModuleListChanged')
end
end
end

function initializeFileAdapterRegistry(obj)
%initializeFileAdapterRegistry Build and populate the FileAdapter registry.
%
% Pinned source ordering (highest to lowest priority):
% top - project's own +fileadapter/ folder
% middle - included optional modules (in IncludedModules order)
% bottom - core module

obj.FileAdapterRegistry = nansen.plugin.fileadapter.Registry();

coreModuleName = string(obj.RequiredModuleName);
coreRoot = fullfile(nansen.common.constant.ModuleRootDirectory, ...
utility.path.packagename2pathstr(coreModuleName));
obj.FileAdapterRegistry.pinBottom(coreModuleName, coreRoot);

% Middle: included modules (excluding the required/core module which
% is already pinned at the bottom).
for i = 1:numel(obj.IncludedModules)
module = obj.IncludedModules(i);
moduleId = string(module.ID);
if moduleId == coreModuleName; continue; end
moduleRoot = fileparts(module.getFileAdapterFolder());
obj.FileAdapterRegistry.addSource(moduleId, moduleRoot);
end

projectModuleRoot = obj.getModuleFolder();
obj.FileAdapterRegistry.pinTop(string(obj.ID), projectModuleRoot);

obj.FileAdapterRegistry.reconcile();
end

function initializeVariableModel(obj)
% initializeVariableModel - Initialize a variable model
filePath = obj.getCatalogPath('VariableModel');
Expand Down
6 changes: 3 additions & 3 deletions code/+nansen/+config/+varmodel/VariableModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function updateDefaultValues(obj)
[obj.Data(:).DataType] = deal('');
for i = 1:numel(obj.Data)
if ~strcmp(obj.Data(i).FileAdapter, 'Default')
isMatch = strcmp({fileAdapterList.FileAdapterName}, obj.Data(i).FileAdapter);
isMatch = [fileAdapterList.FileAdapterName] == string(obj.Data(i).FileAdapter);
if any(isMatch)
fileAdapterFcn = str2func(fileAdapterList(isMatch).FunctionName);
obj.Data(i).DataType = fileAdapterFcn().DataType;
Expand Down Expand Up @@ -300,7 +300,7 @@ function updateDefaultValues(obj)
end

% Find file adapter match for name
isMatch = strcmp({fileAdapterList.FileAdapterName}, variableInfo.FileAdapter);
isMatch = [fileAdapterList.FileAdapterName] == string(variableInfo.FileAdapter);

if ~any(isMatch)
error('File adapter was not found')
Expand Down Expand Up @@ -642,7 +642,7 @@ function disableNotifications(obj)
function variableItem = updateVariableDataType(variableItem)
fileAdapterList = nansen.dataio.listFileAdapters();
if ~strcmp(variableItem.FileAdapter, 'Default')
isMatch = strcmp({fileAdapterList.FileAdapterName}, variableItem.FileAdapter);
isMatch = [fileAdapterList.FileAdapterName] == string(variableItem.FileAdapter);
if any(isMatch)
fileAdapterFcn = str2func(fileAdapterList(isMatch).FunctionName);

Expand Down
22 changes: 12 additions & 10 deletions code/+nansen/+config/+varmodel/VariableModelUI.m
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function delete(obj)
S(j).Subfolder = hRow.SubfolderName.Value;

% Update data type based on fileadapter selection
isMatch = strcmp({fileAdapterList.FileAdapterName}, S(j).FileAdapter);
isMatch = [fileAdapterList.FileAdapterName] == string(S(j).FileAdapter);
if any(isMatch) && ~strcmp( S(j).FileAdapter, 'Default' )
S(j).DataType = fileAdapterList(isMatch).DataType;
end
Expand Down Expand Up @@ -297,9 +297,9 @@ function assignDefaultTablePropertyValues(obj)

if ~isempty(rowData.FileType)
fileAdapterOptions = nansen.dataio.listFileAdapters(rowData.FileType);
fileAdapterOptions = {fileAdapterOptions.FileAdapterName};
fileAdapterOptions = [fileAdapterOptions.FileAdapterName];
else
fileAdapterOptions = {obj.FileAdapterList.FileAdapterName};
fileAdapterOptions = [obj.FileAdapterList.FileAdapterName];
end

hRow.FileAdapterSelect.Items = fileAdapterOptions;
Expand Down Expand Up @@ -446,18 +446,18 @@ function onFileTypeChanged(obj, src, ~)
fileType = lower( strrep(fileType, '.', '') );

fileAdapterOptions = nansen.dataio.listFileAdapters(fileType);
fileAdapterOptions = {fileAdapterOptions.FileAdapterName};
fileAdapterOptions = [fileAdapterOptions.FileAdapterName];

% Update the list of file adapters available for this filetype
if ~isequal(fileAdapterOptions, {'N/A'})
if ~isequal(fileAdapterOptions, "N/A")
hRow.FileAdapterSelect.Items = fileAdapterOptions;

if ~contains(hRow.FileAdapterSelect.Value, fileAdapterOptions)
hRow.FileAdapterSelect.Value = fileAdapterOptions{1};
hRow.FileAdapterSelect.Value = fileAdapterOptions(1);
end
else
hRow.FileAdapterSelect.Items = fileAdapterOptions;
hRow.FileAdapterSelect.Value = fileAdapterOptions{1};
hRow.FileAdapterSelect.Value = fileAdapterOptions(1);
end
end

Expand All @@ -477,13 +477,14 @@ function onFileAdapterChanged(obj, src, evt)
newValue = evt.Value;

fileAdapterList = obj.FileAdapterList;
isMatch = strcmp({fileAdapterList.FileAdapterName}, newValue);
isMatch = [fileAdapterList.FileAdapterName] == string(newValue);

isSupported = false;

if any(isMatch)
supportedFileTypes = fileAdapterList(isMatch).SupportedFileTypes;
if any(ismember(supportedFileTypes, {fileType, ['.' fileType]}))
supportedFileTypesWithDot = "." + strrep(supportedFileTypes, ".", "");
if any(ismember(supportedFileTypesWithDot, "." + string(fileType)))
isSupported = true;
end
end
Expand All @@ -492,7 +493,8 @@ function onFileAdapterChanged(obj, src, evt)
if ~isSupported
hFig = ancestor(obj.Parent, 'figure');
if any(isMatch)
allowedFileTypes = strcat('.', fileAdapterList(isMatch).SupportedFileTypes);
allowedFileTypes = fileAdapterList(isMatch).SupportedFileTypes;
allowedFileTypes = "." + strrep(allowedFileTypes, ".", "");
supportedFileTypes = strjoin(allowedFileTypes, ', ');
else
if strcmp(newValue, 'Default') % Todo: Should not "Default" be part of FileAdapterList?
Expand Down
6 changes: 3 additions & 3 deletions code/+nansen/+config/+varmodel/uiCreateDataVariableFromFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
S = struct();
S.VariableName = '';
S.FileNameExpression = fileName;
S.FileAdapter = fileAdapterList(1).FileAdapterName;
S.FileAdapter_ = {fileAdapterList.FileAdapterName};
S.FileAdapter = char(fileAdapterList(1).FileAdapterName);
S.FileAdapter_ = cellstr([fileAdapterList.FileAdapterName]);
S.Favorite = false;

if ~isempty(options.SkipFields)
Expand Down Expand Up @@ -92,7 +92,7 @@
end

% Get data type from file adapter
fileAdapterIdx = strcmp({fileAdapterList.FileAdapterName}, S.FileAdapter);
fileAdapterIdx = [fileAdapterList.FileAdapterName] == string(S.FileAdapter);

% Normalize to char. Todo: Should support string type
varItem.DataType = char( fileAdapterList(fileAdapterIdx).DataType );
Expand Down
62 changes: 29 additions & 33 deletions code/+nansen/+dataio/listFileAdapters.m
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
function fileAdapterList = listFileAdapters(fileExtension, refresh)
%listFileAdapters Create a list of file adapters
function fileAdapterList = listFileAdapters(fileExtension, options)
%listFileAdapters Return a struct array of available file adapters.
%
% fileAdapterList = nansen.dataio.listFileAdapters() returns a struct
% array containing information about file adapters.
% fileAdapterList = nansen.dataio.listFileAdapters() returns all adapters
% visible to the current project.
%
% The fileAdapterList struct array contains the following fields:
% FileAdapterName (char) : Name of fileadapter
% FunctionName (char) : Name of function for file adapter
% SupportedFileTypes (cell) : File types that are supported with this fileadapter
% DataType (char) : Name of datatype returned by this file adapter

% Todo: Ignore file adapters with a name that are already in the list
% Todo: Start adding from project dir, then watchfolder, then internal?

if nargin < 2 || isempty(refresh); refresh = false; end

project = nansen.getCurrentProject();
if isempty(project); fileAdapterList = struct.empty; return; end

fileAdapterList = table2struct(project.getTable('FileAdapter', refresh));
% fileAdapterList = nansen.dataio.listFileAdapters(fileExtension) filters
% to adapters that support the given extension (with or without a dot).
%
% Each element of fileAdapterList has fields:
% FileAdapterName (string) : Class name of the adapter
% FunctionName (string) : Full MATLAB function/package name
% SupportedFileTypes (string) : Supported file extensions
% DataType (string) : Data type returned on load
% IsDynamic (logical) : true for sidecar-based adapters
%
% Optional name-value arguments:
% Project - Project instance to query (defaults to current project)

if nargin < 1; fileExtension = ''; end
if ~isempty(fileExtension); fileExtension = strrep(fileExtension, '.', ''); end
arguments
fileExtension (1,1) string = ""
options.Project = nansen.getCurrentProject()
end

if ~isempty(fileExtension)
validationFcn = @(extList) any(contains(extList, fileExtension, "IgnoreCase", true));
keep = arrayfun(@(s) validationFcn(s.SupportedFileTypes), ...
fileAdapterList);
else
keep = true(1, numel(fileAdapterList));
if isempty(options.Project)
fileAdapterList = struct.empty;
return
end

fileAdapterList = fileAdapterList(keep);
fileAdapterList = options.Project.FileAdapterRegistry.list(fileExtension);

if isempty(fileAdapterList)
fileAdapterList(1).FileAdapterName = 'N/A';
fileAdapterList(1).FunctionName = '';
fileAdapterList(1).SupportedFileTypes = {};
fileAdapterList(1).DataType = '';
fileAdapterList(1).FileAdapterName = "N/A";
fileAdapterList(1).FunctionName = "";
fileAdapterList(1).SupportedFileTypes = strings(1, 0);
fileAdapterList(1).DataType = "";
end

if ~nargout
Expand All @@ -46,7 +42,7 @@
fileAdapterList.FunctionName = string(fileAdapterList.FunctionName);
fileAdapterList.DataType = string(fileAdapterList.DataType);
fileFormats = fileAdapterList.SupportedFileTypes;
fileFormats = cellfun(@(c) string(strjoin(c, ', ')), fileFormats);
fileFormats = cellfun(@(c) strjoin(string(c), ', '), fileFormats);
fileAdapterList.SupportedFileTypes = fileFormats;
end
end
2 changes: 1 addition & 1 deletion code/+nansen/+metadata/+type/Session.m
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ function updateSessionFolder(obj, dataLocationName, folderPath) %#ok<INUSD>
end

% Get file adapter % Todo: make this more persistent...
isMatch = strcmp({fileAdapterList.FileAdapterName}, variableInfo.FileAdapter);
isMatch = [fileAdapterList.FileAdapterName] == string(variableInfo.FileAdapter);

if ~any(isMatch)
error('NANSEN:Session:FileAdapterNotFound', 'File adapter was not found')
Expand Down
8 changes: 8 additions & 0 deletions code/+nansen/+module/Module.m
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@
itemType = validatestring(itemType, {'SessionMethod', ...
'TableVariable', 'FileAdapter', 'DataVariables', ...
'DataLocations'}, 1);
if strcmp(itemType, 'FileAdapter')
warning('NANSEN:DeprecatedPath:ModuleFileAdapters', ...
['Module.getTable(''FileAdapter'') is deprecated. ', ...
'Use project.FileAdapterRegistry.list() instead.'])
end
itemTable = obj.rehash(itemType, forceRefresh);
end

Expand All @@ -163,6 +168,9 @@
end

function fileAdapterList = get.FileAdapters(obj)
warning('NANSEN:DeprecatedPath:ModuleFileAdapters', ...
['Module.FileAdapters is deprecated. ', ...
'Use project.FileAdapterRegistry.list() instead.'])
itemTable = obj.rehash('FileAdapter');
fileAdapterList = itemTable.FileAdapterName;
fileAdapterList = string(fileAdapterList)';
Expand Down
23 changes: 23 additions & 0 deletions code/+nansen/+plugin/+base/PluginEventData.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
classdef PluginEventData < event.EventData
%PluginEventData Payload for registry plugin events.
%
% Carries the affected entry (for PluginAdded and PluginChanged events)
% and the plugin ID (for PluginRemoved events). Either field may be
% empty depending on the event type.

properties (SetAccess = immutable)
Entry % Struct with parsed adapter fields (Added / Changed events)
PluginId (1,1) string = "" % Adapter name id (Removed events)
end

methods
function obj = PluginEventData(entry, pluginId)
arguments
entry struct = struct.empty
pluginId (1,1) string = ""
end
obj.Entry = entry;
obj.PluginId = pluginId;
end
end
end
Loading