Browse Source

* Patch from Reinier Olislaegers to handle multiline data

git-svn-id: trunk@18027 -
michael 14 years ago
parent
commit
888ea8121c
1 changed files with 66 additions and 7 deletions
  1. 66 7
      packages/fcl-db/src/sdf/sdfdata.pp

+ 66 - 7
packages/fcl-db/src/sdf/sdfdata.pp

@@ -13,6 +13,11 @@ unit SdfData;
 ---------------
 ---------------
 Modifications
 Modifications
 ---------------
 ---------------
+14/Jul/11 BigChimp:
+      Added AllowMultiLine property so user can use fields that have line endings
+      (Carriage Return and/or Line Feed) embedded in their fields (fields need to be
+      quoted). Enabled by default; will break compatibility with earlier versions of
+      SdfData, but using multilines would have resulted in corrupted import anyway.
 12/Mar/04  Lazarus version (Sergey Smirnov AKA SSY)
 12/Mar/04  Lazarus version (Sergey Smirnov AKA SSY)
       Locate and CheckString functions are removed because of Variant data type.
       Locate and CheckString functions are removed because of Variant data type.
       Many things are changed for FPC/Lazarus compatibility.
       Many things are changed for FPC/Lazarus compatibility.
@@ -251,6 +256,8 @@ type
   private
   private
     FDelimiter : Char;
     FDelimiter : Char;
     FFirstLineAsSchema : Boolean;
     FFirstLineAsSchema : Boolean;
+    FFMultiLine         :Boolean;
+    procedure SetMultiLine(const Value: Boolean);
     procedure SetFirstLineAsSchema(Value : Boolean);
     procedure SetFirstLineAsSchema(Value : Boolean);
     procedure SetDelimiter(Value : Char);
     procedure SetDelimiter(Value : Char);
   protected
   protected
@@ -262,6 +269,7 @@ type
   public
   public
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
   published
   published
+    property AllowMultiLine: Boolean read FFMultiLine write SetMultiLine default True; //Whether or not to allow fields containing CR and/or LF
     property Delimiter: Char read FDelimiter write SetDelimiter;
     property Delimiter: Char read FDelimiter write SetDelimiter;
     property FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
     property FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
   end;
   end;
@@ -843,6 +851,7 @@ begin
   inherited Create(AOwner);
   inherited Create(AOwner);
   FDelimiter := ',';
   FDelimiter := ',';
   FFirstLineAsSchema := FALSE;
   FFirstLineAsSchema := FALSE;
+  FFMultiLine :=False;
 end;
 end;
 
 
 procedure TSdfDataSet.InternalInitFieldDefs;
 procedure TSdfDataSet.InternalInitFieldDefs;
@@ -945,7 +954,21 @@ begin
    begin
    begin
 
 
     while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] in [#1..' ']) do
     while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] in [#1..' ']) do
-      Inc(pStrEnd);
+    begin
+     if FFMultiLine=true then
+      begin
+       if ((pStrEnd[0]=CR) or (pStrEnd[0]=LF)) then
+        begin
+         //view this as text, not control characters, so do nothing
+         //todo: check if this is really necessary, probably revert
+         //to original code as quoted case is handled below
+        end;
+      end
+     else
+      begin
+       Inc(pStrEnd);
+      end;
+    end;
 
 
     if not Boolean(Byte(pStrEnd[0])) then
     if not Boolean(Byte(pStrEnd[0])) then
      break;
      break;
@@ -954,10 +977,22 @@ begin
 
 
     if (pStr[0] = '"') then
     if (pStr[0] = '"') then
      begin
      begin
-      repeat
-        Inc(pStrEnd);
-      until not Boolean(Byte(pStrEnd[0])) or
-            ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,CR,LF, #0]));
+      if FFMultiLine=true then
+       begin
+        repeat
+         Inc(pStrEnd);
+        until not Boolean(Byte(pStrEnd[0])) or
+         ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,#0]));
+       end
+      else
+       begin
+        // No multiline, so treat cr/lf as end of record
+         repeat
+          Inc(pStrEnd);
+         until not Boolean(Byte(pStrEnd[0])) or
+          ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,CR,LF, #0]));
+       end;
+
 
 
       if (pStrEnd[0] = '"') then
       if (pStrEnd[0] = '"') then
         Inc(pStr);
         Inc(pStr);
@@ -985,18 +1020,36 @@ begin
 end;
 end;
 
 
 function TSdfDataSet.BufToStore(Buffer: PChar): String;
 function TSdfDataSet.BufToStore(Buffer: PChar): String;
+const
+ QuoteDelimiter='"';
 var
 var
   Str : String;
   Str : String;
   p, i : Integer;
   p, i : Integer;
+  QuoteMe: boolean;
 begin
 begin
   Result := '';
   Result := '';
   p := 1;
   p := 1;
+  QuoteMe:=false;
   for i := 0 to FieldDefs.Count - 1 do
   for i := 0 to FieldDefs.Count - 1 do
   begin
   begin
     Str := Trim(Copy(Buffer, p, FieldDefs[i].Size));
     Str := Trim(Copy(Buffer, p, FieldDefs[i].Size));
     Inc(p, FieldDefs[i].Size);
     Inc(p, FieldDefs[i].Size);
-    if (StrScan(PChar(Str), FDelimiter) <> nil) then
-      Str := '"' + Str + '"';
+    if FFMultiLine=true then
+      begin
+       // If multiline enabled, quote whenever we find carriage return or linefeed
+       if ((QuoteMe=False) and (StrScan(PChar(Str), #10) <> nil)) then QuoteMe:=true;
+       if ((QuoteMe=False) and (StrScan(PChar(Str), #13) <> nil)) then QuoteMe:=true;
+      end
+    else
+      begin
+       // If we don't allow multiline, remove all CR and LF because they mess with the record ends:
+       StringReplace(Str, #10, '', [rfReplaceAll]);
+       StringReplace(Str, #13, '', [rfReplaceAll]);
+      end;
+    // Check for any delimiters occurring in field text
+    if ((QuoteMe=False) and (StrScan(PChar(Str), FDelimiter) <> nil)) then QuoteMe:=true;
+    if (QuoteMe=True) then
+      Str := QuoteDelimiter + Str + QuoteDelimiter;
     Result := Result + Str + FDelimiter;
     Result := Result + Str + FDelimiter;
   end;
   end;
   p := Length(Result);
   p := Length(Result);
@@ -1020,6 +1073,12 @@ begin
   FDataOffset:=Ord(FFirstLineAsSchema);
   FDataOffset:=Ord(FFirstLineAsSchema);
 end;
 end;
 
 
+procedure TSdfDataSet.SetMultiLine(const Value: Boolean);
+begin
+  FFMultiLine:=Value;
+end;
+
+
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 // This procedure is used to register this component on the component palette
 // This procedure is used to register this component on the component palette
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------