Bladeren bron

* Patch from Reinier Olislaegers to handle multiline data

git-svn-id: trunk@18027 -
michael 14 jaren geleden
bovenliggende
commit
888ea8121c
1 gewijzigde bestanden met toevoegingen van 66 en 7 verwijderingen
  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
 ---------------
+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)
       Locate and CheckString functions are removed because of Variant data type.
       Many things are changed for FPC/Lazarus compatibility.
@@ -251,6 +256,8 @@ type
   private
     FDelimiter : Char;
     FFirstLineAsSchema : Boolean;
+    FFMultiLine         :Boolean;
+    procedure SetMultiLine(const Value: Boolean);
     procedure SetFirstLineAsSchema(Value : Boolean);
     procedure SetDelimiter(Value : Char);
   protected
@@ -262,6 +269,7 @@ type
   public
     constructor Create(AOwner: TComponent); override;
   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 FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
   end;
@@ -843,6 +851,7 @@ begin
   inherited Create(AOwner);
   FDelimiter := ',';
   FFirstLineAsSchema := FALSE;
+  FFMultiLine :=False;
 end;
 
 procedure TSdfDataSet.InternalInitFieldDefs;
@@ -945,7 +954,21 @@ begin
    begin
 
     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
      break;
@@ -954,10 +977,22 @@ begin
 
     if (pStr[0] = '"') then
      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
         Inc(pStr);
@@ -985,18 +1020,36 @@ begin
 end;
 
 function TSdfDataSet.BufToStore(Buffer: PChar): String;
+const
+ QuoteDelimiter='"';
 var
   Str : String;
   p, i : Integer;
+  QuoteMe: boolean;
 begin
   Result := '';
   p := 1;
+  QuoteMe:=false;
   for i := 0 to FieldDefs.Count - 1 do
   begin
     Str := Trim(Copy(Buffer, 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;
   end;
   p := Length(Result);
@@ -1020,6 +1073,12 @@ begin
   FDataOffset:=Ord(FFirstLineAsSchema);
 end;
 
+procedure TSdfDataSet.SetMultiLine(const Value: Boolean);
+begin
+  FFMultiLine:=Value;
+end;
+
+
 //-----------------------------------------------------------------------------
 // This procedure is used to register this component on the component palette
 //-----------------------------------------------------------------------------