AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Regionen ersetzen - fertige Funktionen?

Ein Thema von stahli · begonnen am 24. Mär 2011 · letzter Beitrag vom 28. Mär 2011
Antwort Antwort
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#1

Regionen ersetzen - fertige Funktionen?

  Alt 24. Mär 2011, 21:56
Hi Profis,

nachfolgend zitierte Unit (bzw. mehrere von der Sorte) lasse ich automatisch erstellen.
Die gewünschten Strukturen (Objekte, Propertys, Beziehungen) werden vorher in einer Textdatei definiert.
Delphi-Quellcode:
unit odTournamentEvent;

interface

uses
  Classes, SysUtils, od, odOlympicCustom, gOlympic
  {$REGION    'USES'}, odSport, odState, odPlace, odlClubList, odlTournamentList{$ENDREGION 'USES'};

type

  TodTournamentEvent = class(TodOlympicCustom)
  private
{$REGION    '    PRIVAT'}
{$REGION    '    name'}
    FName: String;
{$ENDREGION '    name'}
{$REGION    '    state'}
    FState: TodState;
{$ENDREGION '    state'}
{$REGION    '    sport'}
    FSport: TodSport;
{$ENDREGION '    sport'}
{$REGION    '    place'}
    FPlace: TodPlace;
{$ENDREGION '    place'}
{$REGION    '    clublist'}
    FClubList: TodlClubList;
{$ENDREGION '    clublist'}
{$REGION    '    tournamentlist'}
    FTournamentList: TodlTournamentList;
{$ENDREGION '    tournamentlist'}
{$ENDREGION '    PRIVAT'}
  protected
{$REGION    '    PROTECTED'}
{$REGION    '    name'}
    function get_Name: String; virtual;
    procedure set_Name(const Value: String); virtual;
{$ENDREGION '    name'}
{$ENDREGION '    PROTECTED'}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
{$REGION    '    PUBLIC'}
{$REGION    '    state'}
    [AttrOd]
    property State: TodState read FState;
{$ENDREGION '    state'}
{$REGION    '    sport'}
    [AttrOd]
    property Sport: TodSport read FSport;
{$ENDREGION '    sport'}
{$REGION    '    place'}
    [AttrOd]
    property Place: TodPlace read FPlace;
{$ENDREGION '    place'}
{$REGION    '    clublist'}
    [AttrOd]
    property ClubList: TodlClubList read FClubList;
{$ENDREGION '    clublist'}
{$REGION    '    tournamentlist'}
    [AttrOd]
    property TournamentList: TodlTournamentList read FTournamentList;
{$ENDREGION '    tournamentlist'}
{$ENDREGION '    PUBLIC'}
    procedure MeineGeschäftslogik;
  published
  end;
...

Die in den Regionen befindlichen Teile definieren die Projektdaten und Beziehungen untereinander.
Nachträglich kann ich in den Objekten die Geschäfstlogik sowie beliebige Funktionen und Felder definieren.
Außerdem kann ich z.B. auch die Getter und Setter händisch bearbeiten, um etwa Änderungszeiten zu speichern oder auf bestimmte Änderungen zu reagieren.

Wenn ich spätere Änderungen an den Strukturen vornehme wird eine neue Unit nach obigem Muster erstellt. Diese enthält dann neue oder geänderte Regionen für neue oder geänderte Eigenschaften bzw. weniger Regionen, wenn Eigenschaften entfernt wurden.

Dann müssen die alte und die neue Unit verglichen werden.
Dabei sollen aber nur die definierten Regionen (die in zwei Ebenen existieren) analysiert werden.
Diese Regionen werden dann in der Projektunit korrigiert.
(Sofern darin händische Änderungen vorgenommen wurden (z.B. überschriebener Setter) ist der Bereich von Hand einzupflegen.)

Dazu will ich die Regionen extrahieren und in verschachtelten Objekten abspeichern.

Die Unit würde dann im oberen Bereich so aussehen:
Code:
  TodTournamentEvent = class(TodOlympicCustom)
  private
<<<PRIVAT>>>
  protected
Die erste extrahierte Region so:
Code:
<<<name>>>
<<<state>>>
<<<sport>>>
<<<place>>>
...
Die weiteren Unterregionen enthalten dann den tatsächlichen Code.

Das Tool kann nun analysieren, welche Quelltextteile neu sind oder geändert wurden und daraus eine geänderte Unit erzeugen.
Dazu werden die <<<MARKIERUNGEN>>> wieder durch den Quelltext (mit umschließender Regionen-Deklaration) ersetzt.

In dieser Form erzeuge ich auch bereits die ersten generieten Units. Das funktioniert also schon mal sehr gut.


Mein Problem ist nun, die Regionen zu finden und zu ersetzen.

Ich würde jetzt wohl Pos und PosEx etc. verwenden und "damit herum wurschteln"...

Sieht jemand eine bessere Lösung?
Die IDE macht ja die Regionen-Analyse selbst schon perfekt und erkennt auch noch Kommentierungsbereiche. In gewisser Weise müsste ich das nachbauen.
Kann man auf irgend etwas fertiges zurück greifen?



PS: Bisher habe ich die Unterbereiche in Include-Dateien ausgelagert und diese ggf. später überschrieben. Das ist jedoch beim Debuging teilweise etwas nervig, daher möchte ich lieber auf die Regionen umstellen. Dann gäbe es auch weniger Projektdateien.


[EDIT] Im Bild sieht man, dass der automatisch generierte Code "schön im Hintergrund bleibt". Man kann ihn aber bei Bedarf in bestimmten Bereichen ändern und trotzdem später wieder Strukturänderungen vornehmen.
Miniaturansicht angehängter Grafiken
regions.png  
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (24. Mär 2011 um 22:12 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Regionen ersetzen - fertige Funktionen?

  Alt 25. Mär 2011, 12:18
Sorry für den push, aber vielleicht kann mir eine schlaue Idee ja doch heute Abend viel Arbeit ersparen...

Ich möchte also Start- und Endposition einer Region finden:

ZB. bla bla bla {$REGION 'A'}{$REGION 'b'}bbbbbbbbb{$ENDREGION 'b'} {$REGION 'c'}ccccc{$ENDREGION 'c'}{$ENDREGION 'A'} bla bla bla

Das Problem ist, dass in einer Region andere Regionen und auch Kommentare in geschweiften Klammern enthalten sein können, was die genaue Zuordnung u.U. etwas erschweren könnte.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von Memnarch
Memnarch

Registriert seit: 24. Sep 2010
737 Beiträge
 
#3

AW: Regionen ersetzen - fertige Funktionen?

  Alt 25. Mär 2011, 12:42
Also die Kommentare sollten keine Probleme darstellen.

$Region/$EndRegion sind die einzigen schlüsselwört die du brauchst oder nicht?(natürlich checken obs in {} ist).

Jedesmal wenn du $Region findest geht ein referenzzähler rauf, bei $EndRegion runter. Soweist du ob und in welcher ebene du dich befindest. Leider muss man dafür recht pingelig per Pos/PosEx durchgehen. Also immer nach { suchen, von dort } suchen und dazwischen gucken ob die benötigten schlüsselwörter vorhanden sind.

Das mit dem referenzzähler hab ich mal benutzt als ich nen einfachen Codeanalysator gebastelt hab, der die Codestructur in eine interne Struktur beim textschreiben packte.(war für ne eigene IDE und sone art CodeInsight/Intellisense oder wie man es nennen möchte). Bei prozeduren/funktionen mussten nunmal auch verschachtelungen berücksichtigt werden, damit ich die start und endzeile rausbekam und so den programmcode virtuel in blöcke aufteilen konnte.

MFG
Memnarch
  Mit Zitat antworten Zitat
schlecki

Registriert seit: 11. Apr 2005
Ort: Darmstadt
148 Beiträge
 
Delphi XE2 Enterprise
 
#4

AW: Regionen ersetzen - fertige Funktionen?

  Alt 25. Mär 2011, 13:18
Hallo stahli,

vielleicht nochmal ein ganz anderer Ansatz:

du generierst deine Datei mit den Klassen (wird immer erzeugt!), zusätzlich wird eine weitere Datei erzeugt (aber nur, wenn noch nicht vorhanden!).

Beispiel:
Delphi-Quellcode:
unit odTournamentEventGenerator;
// Diese Datei darf nicht geändert werden,
// sie wird bei Änderungen an der Struktur
// jedes mal neu erzeugt.
{...}
type
  TodTournamentEventGenerated = class(TodOlympicCustom)
    {...}
  end;
Delphi-Quellcode:
unit odTournamentEvent;

// Geschäftslogik kommt in diese Datei.
// Diese wird nur erzeugt, wenn sie noch
// nicht vorhanden ist.

uses
  odTournamentEventGenerator;

interface

type
  TodTournamentEvent = class(TodTournamentEventGenerated)
    {...}
  end;
Das hat den Vorteil, dass du deine Geschäftslogik nicht aus dem generierten Code raussuchen musst.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Regionen ersetzen - fertige Funktionen?

  Alt 25. Mär 2011, 13:45
@Memnarch
Ich hatte, die Hoffnung, das hätte schon jemand irgendwo im Schubfach (aus einem CodeFormatter oder so)...

@Schlecki
Das war mein erster Ansatz. Da sich aber meine Klassen gegenseitig referenzieren (und bereits Instanzen erzeugen), müsste ich die erzeugten Objekte immer casten, um die Geschäftslogik nutzen zu können.
Mein zweiter Ansatz war dann, die Geschäftslogik in ClassHelper zu packen. Das hatte dann aber auch zu viele Einschränkungen und wurde auf Grund der vielzahl der genutzen Klassen mit der Zeit immer unübersichtlicher.
Im dritten Ansatz habe ich die generierten variablen Teile in Include-Dateien ausgelagert. Das hat zwar soweit perfekt funktioniert, ist aber im Handling etwas nervig. Die IDE und der Debuger haben mit den Includes teilw. etwas Probleme, weshalb ich nun die Regionen nutzen will.
Durch die Regionen sollte zum Einen mein Tool die generierten Bereiche erkennen können und diese zum Anderen auch im Quelltext nicht störend auffallen ... denke ich.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Regionen ersetzen - fertige Funktionen?

  Alt 28. Mär 2011, 10:36
Falls es mal jemand brauchen kann, hier mal etwas als Grundlage:

Die Funktion interpretiert Quelltextteile (Code, StringKonstanten, Kommentare). In "Work" erfolgt dann die Verarbeitung der gefundenen Abschnitte.

Natürlich muss das jeder für seine Zwecke anpassen, aber so muss man vielleicht nicht bei 0 anfangen...


Delphi-Quellcode:
procedure TRegion.ScanRegions(var ScanCode: String; var P: Integer; BaseFlag: Boolean);
type
  TCodeType = (ctCode, ctConstant, ctCommentSL, ctCommentEM, ctCommentST);
  // ..............................slash .......embowed......star.......
var
  R, CR: String;
  CodeType: TCodeType;
  ExitFlag: Boolean;

  procedure Work(NewCodeType: TCodeType);
  var
    C, Id: String;
    P1, P2: Integer;
    NewRegion: TRegion;
    SL: TStringList;
    SS: String;
    PP: Integer;
  begin
    case CodeType of
      ctCode:
        begin
        end;
      ctConstant:
        begin
        end;
      ctCommentSL:
        begin
        end;
      ctCommentEM:
        begin
          C := '{$REGION';
          if PosEx(C, UpperCase(R)) = 1 then
          begin
            P1 := PosEx('''', R);
            P2 := PosEx('''', R, Succ(P1));
            if P2 > P1 then
            begin
              NewRegion := TRegion.Create(Self);
              RegionList.Add(NewRegion);
              NewRegion.Id := Copy(R, Succ(P1), Pred(P2 - P1));
              R := NewRegion.IncludeId;
              NewRegion.ScanRegions(ScanCode, P, False);
            end;
          end;
          C := '{$ENDREGION';
          if PosEx(C, UpperCase(R)) = 1 then
          begin
            R := '';
            ExitFlag := not BaseFlag;
            Exit;
          end;
          C := '{$INCLUDE';
          if PosEx(C, UpperCase(R)) = 1 then
          begin
            P1 := PosEx('''', R);
            P2 := PosEx('''', R, Succ(P1));
            if P2 > P1 then
            begin
              NewRegion := TRegion.Create(Self);
              RegionList.Add(NewRegion);
              NewRegion.Id := Copy(R, Succ(P1), Pred(P2 - P1));
              SL := TStringList.Create;
              SL.LoadFromFile(OldPath + NewRegion.Id);
              while Pos('_', NewRegion.Id) > 0 do
                NewRegion.Id := Copy(NewRegion.Id, Succ(Pos('_', NewRegion.Id)), MaxInt);
              while Pos('.', NewRegion.Id) > 0 do
                NewRegion.Id := Copy(NewRegion.Id, 1, Pred(Pos('.', NewRegion.Id)));
              if NewRegion.Id = 'privatvarthen
                NewRegion.Id := 'privat';
              R := NewRegion.IncludeId;
              SS := #13#10 + SL.Text;
              PP := 1;
              NewRegion.ScanRegions(SS, PP, True);
              FreeAndNil(SL);
              NewCodeType := ctCode;
            end;
          end;
        end;
      ctCommentST:
        begin
        end;
    end;
    // R := StringReplace(R, #13#10, ' ', [rfReplaceAll]);
    // OutputDebugString(PChar(R));
    CR := CR + R;
    R := '';
    CodeType := NewCodeType;
  end;

begin
  P := Max(P, 1);
  ExitFlag := False;
  CR := '';
  R := '';
  CodeType := ctCode;
  while P <= Length(ScanCode) do
  begin
    case CodeType of
      ctCode:
        begin
          if Copy(ScanCode, P, 2) = '//then
          begin
            Work(ctCommentSL);
            R := R + Copy(ScanCode, P, 2);
            Inc(P, 2);
          end
          else if Copy(ScanCode, P, 1) = '''then
          begin
            Work(ctConstant);
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end
          else if Copy(ScanCode, P, 1) = '{then
          begin
            Work(ctCommentEM);
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end
          else if Copy(ScanCode, P, 2) = '(*then
          begin
            Work(ctCommentST);
            R := R + Copy(ScanCode, P, 2);
            Inc(P, 2);
          end
          else
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end;
        end;
      ctConstant:
        begin
          if Copy(ScanCode, P, 1) = '''then
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
            Work(ctCode);
          end
          else
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end;
        end;
      ctCommentSL:
        begin
          if Copy(ScanCode, P, 2) = #13#10 then
          begin
            Work(ctCode);
            R := R + Copy(ScanCode, P, 2);
            Inc(P, 2);
          end
          else
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end;
        end;
      ctCommentEM:
        begin
          if Copy(ScanCode, P, 1) = '}then
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
            Work(ctCode);
          end
          else
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end;
        end;
      ctCommentST:
        begin
          if Copy(ScanCode, P, 2) = '*)then
          begin
            R := R + Copy(ScanCode, P, 2);
            Inc(P, 2);
            Work(ctCode);
          end
          else
          begin
            R := R + Copy(ScanCode, P, 1);
            Inc(P);
          end;
        end;
    end;
    if ExitFlag then
    begin
      Code := CR;
      Exit;
    end;
  end;
  Work(ctCode);
  Code := CR;
end;
TRegion ist meine Komponente.
ScanRegions analysiert den Quelltext, der in ScanCode steht.
P muss zu beginn 1 sein.
BaseFlag=True, heißt, dass die Unit (und keine Unterregion) interpretiert wird.
Es werden Quelltext, StringKonstanten und Kommentare herausgelesen.
Die Reaktion auf gefundene Teile erfolgt in Work (das muss also jeder für sich anpassen).
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:23 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz