Files
lazarus-ccr/components/fpspreadsheet/fpsregfileformats.pas
wp_xxyyzz a0ee8b8933 fpspreadsheet:
- Move BasicReader/Writer from fpspreadsheet.pas to fpsReaderWriter.pas in order to fix circular unit dependence introduced in prev commit. 
- Remove usage of temp file in xlsx and ods stream writers (see forum forum.lazarus.freepascal.org/index.php/topic,30606.msg194818.html).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4398 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2015-12-02 22:16:55 +00:00

548 lines
18 KiB
ObjectPascal

{@@ ----------------------------------------------------------------------------
Unit fpsRegFileFormats implements registration of the file formats supported
by fpspreadsheet.
AUTHORS: Felipe Monteiro de Carvalho, Reinier Olislagers, Werner Pamler
LICENSE: See the file COPYING.modifiedLGPL.txt, included in the Lazarus
distribution, for details about the license.
USAGE: Each unit implementing a new spreadsheet format must register the
reader/writer and some specific data by calling "RegisterSpreadFormat".
-------------------------------------------------------------------------------}
unit fpsRegFileFormats;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fpstypes, fpsReaderWriter; //fpspreadsheet;
type
TsSpreadFileAccess = (faRead, faWrite);
function RegisterSpreadFormat(
AFormat: TsSpreadsheetFormat;
AReaderClass: TsSpreadReaderClass;
AWriterClass: TsSpreadWriterClass;
AFormatName, ATechnicalName: String;
const AFileExtensions: array of String): TsSpreadFormatID;
function GetFileFormatFilter(AListSeparator, AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess; const APriorityFormats: array of TsSpreadFormatID;
AllSpreadFormats: Boolean = false; AllExcelFormats: Boolean = false): String;
function GetSpreadFormats(AFileAccess: TsSpreadFileAccess;
const APriorityFormats: array of TsSpreadFormatID): TsSpreadFormatIDArray;
function GetSpreadFormatsFromFileName(AFileAccess: TsSpreadFileAccess; AFileName: TFileName;
APriorityFormat: TsSpreadFormatID = sfidUnknown): TsSpreadFormatIDArray;
function GetSpreadFormatName(AFormatID: TsSpreadFormatID): String;
function GetSpreadTechnicalName(AFormatID: TsSpreadFormatID): String;
function GetSpreadReaderClass(AFormatID: TsSpreadFormatID): TsSpreadReaderClass;
function GetSpreadWriterClass(AFormatID: TsSpreadFormatID): TsSpreadWriterClass;
implementation
uses
fpsStrings;
type
TsSpreadFormatData = class
private
FFormatID: TsSpreadFormatID; // Format identifier
FName: String; // Text to be used in FileDialog filter
FTechnicalName: String; // Text to be used e.g. in Titlebar
FFileExtensions: array of String; // File extensions used by this format
FReaderClass: TsSpreadReaderClass; // Class for reading these files
FWriterClass: TsSpreadWriterClass; // Class for writing these files
function GetFileExtension(AIndex: Integer): String;
function GetFileExtensionCount: Integer;
public
constructor Create(AFormatID: TsSpreadFormatID; AReaderClass: TsSpreadReaderClass;
AWriterClass: TsSpreadWriterClass; AFormatName, ATechnicalName: String;
const AExtensions: Array of String);
// ACanReadFromClipboard, ACanWriteToClipboard: Boolean);
function GetFileFilterMask(ASeparator: Char): String;
// property CanReadFromClipboard: boolean read FCanReadClipboard;
// property CanWriteToClipboard: boolean read FCanWriteClipboard;
property FormatID: TsSpreadFormatID read FFormatID;
property FormatName: String read FName;
property FileExtension[AIndex: Integer]: String read GetFileExtension;
property FileExtensionCount: Integer read GetFileExtensionCount;
property ReaderClass: TsSpreadReaderClass read FReaderClass;
property TechnicalName: String read FTechnicalName;
property WriterClass: TsSpreadWriterClass read FWriterClass;
end;
{ TsSpreadFormatRegistry }
TsSpreadFormatRegistry = class
private
FList: TFPList;
FCachedData: TsSpreadFormatData;
FCachedFormatID: TsSpreadFormatID;
function GetDefaultExt(AFormatID: TsSpreadFormatID): String;
function GetFormatName(AFormatID: TsSpreadFormatID): String;
function GetReaderClass(AFormatID: TsSpreadFormatID): TsSpreadReaderClass;
function GetTechnicalName(AFormatID: TsSpreadFormatID): String;
function GetWriterClass(AFormatID: TsSpreadFormatID): TsSpreadWriterClass;
protected
function Add(AData: TsSpreadFormatData): Integer;
function FindFormatID(AFormatID: TsSpreadFormatID): TsSpreadFormatData;
function IndexOf(AFormatID: TsSpreadFormatID): Integer;
public
constructor Create;
destructor Destroy; override;
function GetAllSpreadFilesMask(AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess): String;
function GetAllExcelFilesMask(AExtSeparator: Char): String;
function GetFileFilter(AListSeparator, AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess; const APriorityFormats: array of TsSpreadFormatID;
AllSpreadFormats: Boolean = false; AllExcelFormats: Boolean = false): String;
function GetFormatArray(AFileAccess: TsSpreadFileAccess;
const APriorityFormats: array of TsSpreadFormatID): TsSpreadFormatIDArray;
function GetFormatArrayFromFileName(AFileAccess: TsSpreadFileAccess;
const AFileName: String; APriorityFormat: TsSpreadFormatID = sfidUnknown): TsSpreadFormatIDArray;
property DefaultExt[AFormatID: TsSpreadFormatID]: String read GetDefaultExt;
property FormatName[AFormatID: TsSpreadFormatID]: String read GetFormatName;
property ReaderClass[AFormatID: TsSpreadFormatID]: TsSpreadReaderClass read GetReaderClass;
property TechnicalName[AFormatID: TsSpreadFormatID]: String read GetTechnicalName;
property WriterClass[AFormatID: TsSpreadFormatID]: TsSpreadWriterClass read GetWriterClass;
end;
var
SpreadFormatRegistry: TsSpreadFormatRegistry;
{==============================================================================}
{ TsSpreadFormatData }
{==============================================================================}
constructor TsSpreadFormatData.Create(AFormatID: TsSpreadFormatID;
AReaderClass: TsSpreadReaderClass; AWriterClass: TsSpreadWriterClass;
AFormatName, ATechnicalName: String; const AExtensions: array of String);
var
i: Integer;
begin
FFormatID := AFormatID;
FReaderClass := AReaderClass;
FWriterClass := AWriterClass;
FName := AFormatName;
FTechnicalName := ATechnicalName;
SetLength(FFileExtensions, Length(AExtensions));
for i:=0 to High(FFileExtensions) do FFileExtensions[i] := AExtensions[i];
end;
function TsSpreadFormatData.GetFileExtension(AIndex: Integer): String;
begin
Result := FFileExtensions[AIndex];
end;
function TsSpreadFormatData.GetFileExtensionCount: Integer;
begin
Result := Length(FFileExtensions);
end;
function TsSpreadFormatData.GetFileFilterMask(ASeparator: Char): String;
var
i: Integer;
begin
Result := '*' + FFileExtensions[0];
for i:= 1 to High(FFileExtensions) do
Result := Result + ASeparator + '*' + FFileExtensions[i];
end;
{==============================================================================}
{ TsSpreadFormatRegistry }
{==============================================================================}
constructor TsSpreadFormatRegistry.Create;
begin
inherited;
FList := TFPList.Create;
FCachedFormatID := sfidUnknown;
FCachedData := nil;
end;
destructor TsSpreadFormatRegistry.Destroy;
var
i: Integer;
begin
for i := FList.Count-1 downto 0 do TObject(FList[i]).Free;
FList.Free;
inherited;
end;
function TsSpreadFormatRegistry.Add(AData: TsSpreadFormatData): Integer;
begin
Result := FList.Add(AData);
end;
function TsSpreadFormatRegistry.FindFormatID(AFormatID: TsSpreadFormatID): TsSpreadFormatData;
var
idx: Integer;
begin
if AFormatID <> FCachedFormatID then
begin
idx := IndexOf(AFormatID);
if idx = -1 then
begin
FCachedData := nil;
FCachedFormatID := sfidUnknown;
end else
begin
FCachedData := TsSpreadFormatData(FList[idx]);
FCachedFormatID := AFormatID;
end;
end;
Result := FCachedData;
end;
function TsSpreadFormatRegistry.GetDefaultExt(AFormatID: TsSpreadFormatID): String;
var
data: TsSpreadFormatData;
begin
data := FindFormatID(AFormatID);
if data <> nil then
Result := data.FileExtension[0] else
Result := '';
end;
function TsSpreadFormatRegistry.GetAllSpreadFilesMask(AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess): String;
var
L: TStrings;
data: TsSpreadFormatData;
ext: String;
i, j: Integer;
begin
Result := '';
L := TStringList.Create;
try
for i:=0 to FList.Count-1 do
begin
data := TsSpreadFormatData(FList[i]);
case AFileAccess of
faRead : if data.ReaderClass = nil then continue;
faWrite : if data.WriterClass = nil then continue;
end;
for j:=0 to data.FileExtensionCount-1 do
begin
ext := data.FileExtension[j];
if L.IndexOf(ext) = -1 then
L.Add(ext);
end;
end;
if L.Count > 0 then
begin
Result := '*' + L[0];
for i := 1 to L.Count-1 do
Result := Result + AExtSeparator + '*' + L[i];
end;
finally
L.Free;
end;
end;
function TsSpreadFormatRegistry.GetAllExcelFilesMask(AExtSeparator: Char): String;
var
L: TStrings;
begin
L := TStringList.Create;
try
if (IndexOf(ord(sfExcel8)) <> -1) or
(IndexOf(ord(sfExcel5)) <> -1) or
(IndexOf(ord(sfExcel2)) <> -1) then L.Add('*.xls');
if (IndexOf(ord(sfOOXML)) <> -1) then L.Add('*.xlsx');
L.Delimiter := AExtSeparator;
L.StrictDelimiter := true;
Result := L.DelimitedText;
finally
L.Free;
end;
end;
function TsSpreadFormatRegistry.GetFileFilter(AListSeparator, AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess; const APriorityFormats: array of TsSpreadFormatID;
AllSpreadFormats: Boolean = false; AllExcelFormats: Boolean = false): String;
var
i, idx: Integer;
L: TStrings;
s: String;
data: TsSpreadFormatData;
begin
// Bring the formats listed in APriorityFormats to the top
if Length(APriorityFormats) > 0 then
for i := High(APriorityFormats) downto Low(APriorityFormats) do
begin
idx := IndexOf(APriorityFormats[i]);
data := TsSpreadFormatData(FList[idx]);
FList.Delete(idx);
FList.Insert(0, data);
end;
L := TStringList.Create;
try
L.Delimiter := AListSeparator;
L.StrictDelimiter := true;
if AllSpreadFormats then
begin
s := GetAllSpreadFilesMask(AExtSeparator, AFileAccess);
if s <> '' then
begin
L.Add(rsAllSpreadsheetFiles);
L.Add(GetAllSpreadFilesMask(AExtSeparator, AFileAccess));
end;
end;
if AllExcelFormats then
begin
s := GetAllExcelFilesMask(AExtSeparator);
if s <> '' then
begin
L.Add(Format('%s (%s)', [rsAllExcelFiles, s]));
L.Add(s);
end;
end;
for i:=0 to FList.Count-1 do
begin
data := TsSpreadFormatData(FList[i]);
case AFileAccess of
faRead : if data.ReaderClass = nil then Continue;
faWrite : if data.WriterClass = nil then Continue;
end;
s := data.GetFileFilterMask(AExtSeparator);
L.Add(Format('%s %s (%s)', [data.FormatName, rsFiles, s]));
L.Add(s);
end;
Result := L.DelimitedText;
finally
L.Free;
end;
end;
function TsSpreadFormatRegistry.GetFormatArray(AFileAccess: TsSpreadFileAccess;
const APriorityFormats: array of TsSpreadFormatID): TsSpreadFormatIDArray;
var
i, n, idx: Integer;
data: TsSpreadFormatData;
begin
// Rearrange the formats such the one noted in APriorityFormats are at the top
if Length(APriorityFormats) > 0 then
for i := High(APriorityFormats) downto Low(APriorityFormats) do
begin
idx := IndexOf(APriorityFormats[i]);
data := TsSpreadFormatData(FList[idx]);
FList.Delete(idx);
FList.Insert(0, data);
end;
SetLength(Result, FList.Count);
n := 0;
for i := 0 to FList.Count-1 do
begin
data := TsSpreadFormatData(FList[i]);
case AFileAccess of
faRead : if data.ReaderClass = nil then Continue;
faWrite : if data.WriterClass = nil then Continue;
end;
Result[n] := data.FormatID;
inc(n);
end;
SetLength(Result, n);
end;
function TsSpreadFormatRegistry.GetFormatArrayFromFileName(
AFileAccess: TsSpreadFileAccess; const AFileName: String;
APriorityFormat: TsSpreadFormatID = sfidUnknown): TsSpreadFormatIDArray;
var
idx: Integer;
i, n: Integer;
ext: String;
data: TsSpreadFormatData;
begin
ext := Lowercase(ExtractFileExt(AFileName));
if APriorityFormat <> sfidUnknown then
begin
// Bring the priority format to the top
idx := IndexOf(APriorityFormat);
FList.Exchange(0, idx);
end;
SetLength(Result, FList.Count);
n := 0;
for i := 0 to FList.Count - 1 do
begin
data := TsSpreadFormatData(FList[i]);
case AFileAccess of
faRead : if data.ReaderClass = nil then Continue;
faWrite : if data.WriterClass = nil then Continue;
end;
if Lowercase(data.FileExtension[0]) = ext then
begin
Result[n] := data.FormatID;
inc(n);
end;
end;
SetLength(Result, n);
if APriorityFormat <> sfidUnknown then
// Restore original order
FList.Exchange(idx, 0);
end;
function TsSpreadFormatRegistry.GetFormatName(AFormatID: TsSpreadFormatID): String;
var
data: TsSpreadFormatData;
begin
data := FindFormatID(AFormatID);
if data <> nil then
Result := data.FormatName else
Result := '';
end;
function TsSpreadFormatRegistry.GetReaderClass(AFormatID: TsSpreadFormatID): TsSpreadReaderClass;
var
data: TsSpreadFormatData;
begin
data := FindFormatID(AFormatID);
if data <> nil then
Result := data.ReaderClass else
Result := nil;
end;
function TsSpreadFormatRegistry.GetTechnicalName(AFormatID: TsSpreadFormatID): String;
var
data: TsSpreadFormatData;
begin
data := FindFormatID(AFormatID);
if data <> nil then
Result := data.TechnicalName else
Result := '';
end;
function TsSpreadFormatRegistry.GetWriterClass(AFormatID: TsSpreadFormatID): TsSpreadWriterClass;
var
data: TsSpreadFormatData;
begin
data := FindFormatID(AFormatID);
if data <> nil then
Result := data.WriterClass else
Result := nil;
end;
function TsSpreadFormatRegistry.IndexOf(AFormatID: TsSpreadFormatID): Integer;
begin
for Result := 0 to FList.Count - 1 do
if TsSpreadFormatData(FList[Result]).FormatID = AFormatID then
exit;
Result := -1;
end;
{==============================================================================}
{ Public utility functions }
{==============================================================================}
{@@ ----------------------------------------------------------------------------
Registers a new reader/writer pair for a given spreadsheet file format
AFormat identifies the file format, see sfXXXX declarations in built-in
fpstypes.
The system is open to user-defined formats. In this case, AFormat must have
the value "sfUser". The format identifier is calculated as a negative number,
stored in the TsSpreadFormatData class and returned as function result.
This value is needed when calling fpspreadsheet's ReadFromXXXX and WriteToXXXX
methods to specify the file format.
-------------------------------------------------------------------------------}
function RegisterSpreadFormat(AFormat: TsSpreadsheetFormat;
AReaderClass: TsSpreadReaderClass; AWriterClass: TsSpreadWriterClass;
AFormatName, ATechnicalName: String; const AFileExtensions: array of String): TsSpreadFormatID;
var
fmt: TsSpreadFormatData;
n: Integer;
begin
if AFormat <> sfUser then begin
n := SpreadFormatRegistry.IndexOf(ord(AFormat));
if n >= 0 then
raise Exception.Create('[RegisterSpreadFormat] Spreadsheet format is already registered.');
end;
if Length(AFileExtensions) = 0 then
raise Exception.Create('[RegisterSpreadFormat] File extensions needed for registering a file format.');
if (AFormatName = '') or (ATechnicalName = '') then
raise Exception.Create('[RegisterSpreadFormat] File format name is not specified.');
fmt := TsSpreadFormatData.Create(ord(AFormat), AReaderClass, AWriterClass,
AFormatName, ATechnicalName, AFileExtensions);
n := SpreadFormatRegistry.Add(fmt);
if (AFormat = sfUser) then
begin
if (n <= ord(sfUser)) then n := n + ord(sfUser) + 1;
fmt.FFormatID := -n;
end;
Result := fmt.FormatID;
end;
function GetFileFormatFilter(AListSeparator, AExtSeparator: Char;
AFileAccess: TsSpreadFileAccess; const APriorityFormats: array of TsSpreadFormatID;
AllSpreadFormats: Boolean = false; AllExcelFormats: Boolean = false): String;
begin
Result := SpreadFormatRegistry.GetFileFilter(AListSeparator, AExtSeparator,
AFileAccess, APriorityFormats, AllSpreadFormats, AllExcelFormats);
end;
function GetSpreadFormats(AFileAccess: TsSpreadFileAccess;
const APriorityFormats: array of TsSpreadFormatID): TsSpreadFormatIDArray;
begin
Result := SpreadFormatRegistry.GetFormatArray(AFileAccess, APriorityFormats);
end;
function GetSpreadFormatsFromFileName(
AFileAccess: TsSpreadFileAccess; AFileName: TFileName;
APriorityFormat: TsSpreadFormatID = sfidUnknown): TsSpreadFormatIDArray;
begin
Result := SpreadFormatRegistry.GetFormatArrayFromFileName(
AFileAccess, AFileName, APriorityFormat);
end;
function GetSpreadFormatName(AFormatID: TsSpreadFormatID): String;
begin
Result := SpreadFormatRegistry.FormatName[AFormatID];
end;
function GetSpreadTechnicalName(AFormatID: TsSpreadFormatID): String;
begin
Result := SpreadFormatRegistry.TechnicalName[AFormatID];
end;
function GetSpreadReaderClass(AFormatID: TsSpreadFormatID): TsSpreadReaderClass;
begin
Result := SpreadFormatRegistry.ReaderClass[AFormatID];
end;
function GetSpreadWriterClass(AFormatID: TsSpreadFormatID): TsSpreadWriterClass;
begin
Result := SpreadFormatRegistry.WriterClass[AFormatID];
end;
initialization
SpreadFormatRegistry := TsSpreadFormatRegistry.Create;
finalization
SpreadFormatRegistry.Free;
end.