#!/usr/bin/perl
# Analysiert javap-Ausgaben (-c Disassemblierung, -p alle Signaturen) zur .class-Datei.
# Dadurch, dass nicht auf der .java-Datei gearbeitet wird, hat man bestimmte Format-Probleme nicht,
# Anwendung: nach Aufr�umarbeiten hilfreich, um zu erkennen, welche Klassen/Methoden nicht mehr gebraucht werden, weil
# sie in einer bestimmten Umgebung (z.B Liste von Verzeichnissen) nicht mehr aufgerufen werden. Hier bietet Eclipse
# keine Unterst�zung an !

# Sinn: erkennt, 
# a) welche Methoden einer Klasse sich selbst aufrufen, wenn MAUFRUFE=1 
# b) welche privaten Methoden/Attribute nicht aufgerufen werden, also entfernt werden koennen (wie Eclipse)
# c) welche public/protected Methoden und Attribute gar nicht aufgerufen werden,
# wobei dies wegen Reflection keine endgueltige Aussage ist; javap kann leider auch nicht @Override erkennen,
# was ja zwingend eine Implementierung noetig macht 

# Sep. 2013: Anpassung fuer Linux, kleine Erweiterungen, auch Punkt c)
# August 2014: Korrekturen bei enum und abstract; Performanz
# Sept. 2014: Hinweis bei Methoden mit moeglichem static-Zugriff, also ohne Zugriff auf Objektzustand
# Mai 2015: nicht direkt aufgerufe Ereignismethoden von swing behandelt
# August 2019: Anpassung an multicatch, try-Resource von java 7; default-interface

if ($#ARGV < 0)
{
  die "Aufruf: perl <Klasse | Verzeichnis | \@Inhaltsdatei>. \n";
}

# Umgebung unterscheiden
if ($^O =~ /win/i)
{
  $TR="\\"; $klassenpfad=";%CLASSPATH%"; $loesche = "del";
}
else
{
  $TR="/"; $klassenpfad=":\$CLASSPATH"; $loesche = "rm";
}

my $DEBUG=1;  # Zwischenst�nde anzeigen
my $MAUFRUFE = 0;  # Aufrufe der Methoden anzeigen
my $PUT = 1;  # wahr, wenn Wert gesetzt und auf verwendet entscheiden; sonst nur bei Lesen; bei JPA-Entity auf 1
my $ohneGetterSetter = 1;  # wahr, wenn get/set nicht geprüft werden soll

# Liste der Java-Modifikatoren
%modif=(
"private"=> 1, "protected"=> 1, "public"=> 1, "native"=> 1, "strictfp"=> 1,
"static"=> 1 ,"final"=> 1,"transient"=> 1,"volatile"=> 1,"synchronized"=> 1, "abstract"=> 1, "default"=>1
);
# Liste der primitiven Datentypen
%primitiv =
(
 "int" => 1, "long"=> 1, "byte"=> 1, "short"=> 1, "float"=> 1, 
 "double"=> 1, "boolean"=> 1, "char"=> 1, "void"=> 1);


my %alleBezeichner = ();
my %oberklassen = ();
# abstrakte Methoden und enum-Methoden nicht auszugeben, also hier zu merken
my %enums = ();  

if ($#ARGV < 0)
{
  die "Keine Datei oder Verzeichnis angegeben $ARGV[0]\n";
}
# verarbeitet eine Anzahl von Dateien oder Verzeichnisse oder eine Datei mit einer Liste
$startZeit = time();
&verarbeite_eingaben(\@ARGV);
if (-e "temp.xxx") { system("$loesche temp.xxx"); }
# Nach durchlaufen aller Class-Dateien nicht gefundenen Bezeichner ausgeben
&polymorphismus_abschliessen();
$endeZeit = time();
&ausgabe_ungenutzt("GLOBAL");
print "Zeitdifferenz in msec.: " . ($endeZeit - $startZeit). "\n";

#
# Abschnitt Methoden
#


# liest die Eingabeparameter ein. Vorsicht: keine Vorsorge auf Endlosrekursion getroffen  !
sub verarbeite_eingaben()
{
  my $listeR = $_[0];  # my @liste = @{$_[0]}; # Dereferenz !

  foreach $obj (@$listeR)
  {
    if (substr($obj, 0,1) eq "@")
    {
      # Liste mit Eintr�gen
      my @tmp = ();
      open (my $WO, "<$obj") || die "Datei mit Listeninhalte $obj nicht gefunden\n";
      while (<$WO>)
      {
        chomp;
        push @tmp, $_;
      }
      close $WO;
      &verarbeite_eingaben(\@tmp);
    }
    elsif (-f "$obj" || -f "$obj.class")
    {
      # konkrete class-Datei
      &verarbeite($obj);
    }
    elsif (-d "$obj")
    {
      # Verzeichnis, in dem die class-Dateien liegen
      &verzeichnis_lesen($obj);
    }
    else
    {
      print "Weder Datei noch Verzeichnis: $obj\n"; <STDIN>;
    }
  }
}


# liest alle Class-Dateien des Verzeichnisses ein, auch rekursiv
# Unterklassen oder lambdas mit Dollar (java 8) werden zuerst gelesen
sub verzeichnis_lesen
{
  my $name = $_[0]; if (substr($name, length($name)-1,1) eq $TR) { $name = substr($name,0,length($name)-1); }
  my $result = opendir(my $verz, $name);
  my @listeDat = ();
  if (! $result) { print "Verzeichnis '$name' nicht gefunden\n"; return; }
  while (my $d = readdir($verz) )
  {
    my $weiter = "$name$TR$d";
    if ( -f $weiter && $d =~ /.class$/  && $d !~ /^Idl_/)
    {
      if (index($weiter,"\$") < 0) {
        for (my $i = $#listeDat; $i >= 0; $i--) { &verarbeite($listeDat[$i]); pop @listeDat; }
      }
      push @listeDat, $weiter;
    }
    elsif ($d ne "." && $d ne ".." &&  -d $weiter)
    {
      &verzeichnis_lesen($weiter);
    }
  } # Ende Verzeichnis lesen
  closedir ($verz);
  for (my $i = $#listeDat; $i >= 0; $i--) { &verarbeite($listeDat[$i]); }
}


sub rufeJavapAuf
{
  my $klasse = $_[0];
  if (index($klasse, ".class") >= 0) # javap mag kein .class
  {
    $von = rindex($klasse, "."); $klasse = substr($klasse, 0, $von);
  }
  # Pfad-Angaben rausmachen
  $von = rindex($klasse, $TR); my $pfad = ".";
  if ($von >= 0)
  {
    $pfad = substr($klasse, 0, $von); $klasse = substr($klasse, $von+1);
  }
  print "\n$klasse:\n";
  if ($TR ne "\\") { $klasse =~ s/\$/\\\$/g; }  # unter linux $ Sonderzeichen !
  $anw = "javap -classpath $pfad$klassenpfad -p -c $klasse > temp.xxx 2> /dev/null";
  $erg = system ($anw);
  if ($erg != 0)
  {
    die "javap endete mit Rueckgabewert $erg\n";
  }
}


# Verarbeitet eine Klasse
sub verarbeite
{
  my $klasse = $_[0];
  
  $klasse =~ s/\.class//;
  rufeJavapAuf($klasse);  # Nachzustand: temp.xxx besteht mit Inhalten
  if ($TR eq "\\") { $klasse =~ s/\\/\./g; } else { $klasse =~ s/\//\./g; }
  my %methode; my %attribut;  # nur private speichern, am Ende der Datei rauswerfen
  my $packet = ""; my $ober = "";
  #print("   Klasse ist $klasse\n");
  if (0 == signaturenErmitteln(\$klasse,\%methode, \%attribut, \$packet, \$ober)) {
    methodeninhaltePruefen($klasse, \%methode, \%attribut, \$packet, \$ober);
  }
}


sub signaturenErmitteln
{
  my $klasse = ${$_[0]};  # Ref., da dieser ggf. gekuerzt wird

  # 1) Ausgabe (-p) durchlesen, Strukturen aufbauen
  # Attributnamen und Methoden der eigenen Klasse vermerken(=0), Attributtypen, Typen in Methoden(als Liste) als gebraucht merken (=1)
  open (DAT, "<temp.xxx") || die "temp.xxx nicht gefunden";
  my $methodeR = $_[1]; my $attributR = $_[2]; my %zuordG = ();
  my $packet = ${$_[3]}; my $packetSpez = ""; my $ober = ${$_[4]}; # my $klasseRef = "";
  $packet = ""; $packetSpez = "";
  my $vonWindowListener = 0; my $vonActionListener = 0; my $vonKeyListener = 0; my $vonMouseListener = 0;
  while (<DAT>)
  {
    chomp;

    # Klassen, Interface auswerten
    if ($packet eq "" && $_ =~ /(^| )(class|interface) .+( extends | implements )?.* {$/)
    {
      $_ = &generics_behandeln_klasse($_, \%zuordG);
      #print(%zuordG);
      # Packetbezeichnung holen, Klassenname einkuerzen
      $bis = rindex($klasse, "."); $klasse = ($bis >= 0) ? substr($klasse, $bis+1) : $klasse; # Aenderung!
      $bis = index($_, "$klasse ");
      $bis = index($_, "$klasse<") unless $bis >= 0; # Generics
      die "Klasse/Interface $klasse in $_ nicht gefunden\n" unless $bis >= 0;
      # die (optionale) Paket-Angabe befindet sich jetzt zwischen Leerzeichen und Namensanfang
      $von = rindex($_, " ", $bis);
      if ($von + 1 < $bis)
      {
        $packet = substr($_, $von+1, $bis - $von - 1); $packetSpez = $packet;
        $packetSpez =~ s/./\//g;  # Punkt durch / ersetzen, wg. unten
        print "Paketname ist $packet, " unless !$DEBUG;
      }

      # wenn abgeleitet, dann die Basis-IF als genutzt kennzeichnen
      # Muster: public class KlasseA extends java.lang.Object implements Inf1,java.io.Serializable{
      my $von = index($_, " extends ");
      if ($von >= 0)
      {
        # n Ableitung (1 bei Klasse, n bei interface), ggf. nur Object (begr. durch " |{")
        $von = index($_, " extends ")+9; $bis = index($_, " ", $von); $bis = index($_, "{", $von) unless $bis >= 0;
        die "Klassenende nicht gef." unless $bis >= 0;
        $ober = substr($_, $von, $bis-$von);
        $oberklassen{"$packet$klasse"} = $ober;  # wegen Polymorphismus verspeichern !
        # print "$packet$klasse --> $ober\n";
        # Sonderfall enum vermerken, da values() und valueOf(java.lang.String) oft nicht genutzt werden
        # Anmerkung: wenn keine direkte, sondern indirekte Ableitung von enum, muss dies nicht erkannt werden "
        if ($ober eq "java.lang.Enum") { $enums{"$packet$klasse"} = 1; }
        &liste_gebraucht_merken2($ober, $packet, $klasse, "t");
      }

      $von = index($_, " implements ");
      if ($von >= 0)
      {
        # n Implementierungen bestimmen, begr. durch "{"
        $von = index($_, " implements ") + 12; $bis = index($_, "{", $von);
        die "Interfaceende nicht gef." unless $bis >= 0;
        &liste_gebraucht_merken2(substr($_, $von, $bis-$von), $packet, $klasse, "t");
        # Sonderfall swing: von Ableitung WindowListener erkennen
        $vonWindowListener = (index($_, "java.awt.event.WindowListener") >= 0);
        $vonActionListener = (index($_, "java.awt.event.ActionListener") >= 0);
        $vonKeyListener = (index($_, "java.awt.event.KeyListener") >= 0);
        $vonMouseListener = (index($_, "java.awt.event.MouseListener") >= 0);
      }

      # diese Zeile soll nicht weiterverarbeitet werden
      if ($klasse eq "" || $klasse =~ /\.$/) { print "Fehler: $packet,$klasse in $_\n"; <STDIN>; }
      $alleBezeichner{"$packet$klasse"} += 0;
      next;  # wegen java8-default-Impl. auch Interface durchsuchen !
    }

    # Deklarationen enden Strichpunkt, keine Referenzen
    if ($_ !~ /; *$/) { next; }
    if ($_ =~ / this\$[\d]+;/) { 
       # Klassenverweis auf uebergeordnete Klasse: erscheint als erster Parameter in den Konstruktoren
       #   final testp.CatchTest this$0;
       #my $bis = rindex($_, " this"); my $von = rindex($_, " ", $bis-1);
       #$klasseRef = substr($_, $von+1, $bis-$von-1); print("KlasseRef $klasseRef");  # TODO was damit ? Zugriff Variablen
      next;
    }
    if ($_ =~ /[\d]+:/) { next; } # Codeabsch. kann auch ; haben


    # Attributtypen/Methodenrueckgaben ermitteln.
    # Modifizierer ignorieren, private besonders, da am Klassenende gleich entfernbar
    # Anmerkung: final static mit primitiven Datentypen oder String wird gleich aufgel�st, erscheint also
    # nicht in javap-Aufruf; static final-Methoden werden von javac nicht so inline eingesetzt
    $_ = (index ($_,"(") < 0) ? 
       &generics_behandeln_attribut($_, \%zuordG) : 
       &generics_behandeln_methode($_, \%zuordG);
    $weiter = index ($_,"("); # Achtung: In Parameterlisten k�nnen Leerzeichen sein
    my $priv = index(substr($_,0,$weiter), "private ") >= 0 ? 1 : 0 ; 
    my @test = split(" ", substr($_,0,$weiter));  # ist durch Leerzeichen getrennt, kann aber auch in Map<A, B> sein !
    my $rueckTyp = "";
    for (my $i = $#test-1; $i >= 0; --$i) {
      if (exists $modif{$test[$i]}) { last; }
      if ($rueckTyp ne "") { $rueckTyp = " ".$rueckTyp; }
      $rueckTyp = $test[$i].$rueckTyp;
    }
    #print("Rückgabetyp $rueckTyp\n");

    my $eintragen = 1;
    if ($weiter > 0)
    {
      # varargs ersetzen: "..." => "[]"
      $_ =~ s/\.\.\./\[\]/g;
      # Methode. Signatur auch mitnehmen wg. �berladung, aber ohne hinter ) opt. throws-Klausel
      $bis = rindex($_, ")");
      if (index($_, "$packet$klasse(") >= 0)
      {
        $name = "\"<init>\"".substr($_,$weiter, $bis-$weiter+1);  # Konstruktor, kein R�ckgabewert     
      }
      else
      {
        &liste_gebraucht_merken2($rueckTyp, $packet, $klasse, "t");  # R�ckgabewert wird verwendet
        $von = rindex($_, " ", $weiter); $name = substr($_, $von+1, $bis-$von);
      }
      &liste_gebraucht_merken2(substr($_, $weiter+1, $bis-$weiter-1), $packet, $klasse, "t"); # Kommaliste Parametertypen
      $methodeR->{"$name"} = 0 unless !$priv || $name =~ /^lambda\$/;
      # Sonderfall Callback-Methoden swing
      &swingPruefen($ober,$_,$packet,$klasse,$name,$vonWindowListener,$vonActionListener,$vonKeyListener,$vonMouseListener);

      my $bis2 = index($_, " throws ", $bis);
      if ($bis2 >= 0)
      {
        my $bis3 = index($_, ";", $bis2); die "throws nicht abgeschlossen\n" unless $bis3 >= 0;
        &liste_gebraucht_merken2(substr($_, $bis2 + 8, $bis3-$bis2-8), $packet, $klasse, "t"); # eine/mehrere Exception-Typen !
      }
    }
    else
    {
      # Attribut - primitive mit static final nicht �bernehmen, weil javac diese direkt einsetzt
      if (index($_, " static ") >= 0 && index($_, " final ") >= 0 &&
         ($rueckTyp eq "java.lang.String" || exists $primitiv{"$rueckTyp"}))
      {
        #print "Ignoriert wird: $_\n"; <STDIN>;
        $eintragen = 0; # javac loest dies inline auf !
      }
      else
      { 
        # (f. Wert gesetzt, zuTun: pr�fen). Klassen wie BigInteger erscheinen sp�ter.
        if ($bis < 0 && index($_, "{}") >= 0) { next; } # static block
        die "Ung�ltiges Attribut $_\n" unless $bis >= 0;
        &liste_gebraucht_merken2($rueckTyp, $packet, $klasse, "t");  # Typ wird verwendet
        $von = rindex($_, " "); $bis = rindex($_,";");
        $name = substr($_, $von+1, $bis-$von-1);
        $attributR->{"$name"} = 0 unless !$priv;
      }
    }
    if ($eintragen && $name eq "") { print "Fehler: $packet,$klasse,$name in $_\n"; <STDIN>; }
    $alleBezeichner{"$packet$klasse.$name"} += 0 unless !$eintragen || $name =~ /^lambda\$/;
  }

  if ($DEBUG) { print "Nach Schritt 1: \n"; &testausgabe(); }
  return 0;
}


sub generics_behandeln_methode
{
  my $zeile = $_[0]; my $zuordGR = $_[1];

  # Bsp. Methode: public <T extends java.lang.Number & java.io.Serializable> testp.TestException2 test2(T);
  # public <R> java.lang.String test1(R);
  if (index($zeile, "(") < 0) { return $zeile; }
  # Bei T ist Object zu nehmen; bei extends der Basistyp; Rueckgabewert kann z.B. auch < haben wie List<String>
  my $sammel = ""; my $istGen = 0;
  my $bis = index($zeile, "(");
  if (index(substr($zeile,0,$bis), "<") >= 0) {
    my @test = split(" ", substr($zeile,0,$bis));  # ist durch Leerzeichen getrennt, kann aber auch in Map<A, B> sein !
    for (my $i = 0; $i <= $#test; ++$i) {
      if (exists $modif{$test[$i]}) { next; }
      if (!$istGen && substr($test[$i],0,1) eq "<") { $istGen = 1; $sammel = $test[$i]; }
      if ($istGen && substr($test[$i],0,1) ne "<") { $sammel = $sammel." ".$test[$i]; } 
      if ($istGen && $sammel =~ />$/) { $istGen = 2; last; }
    }
    die "Generics-Ende nicht gefunden in $zeile" unless $sammel eq "" || $istGen == 2;
    #print("!!! Generics $sammel\n") unless $sammel eq "";

    if ($sammel ne "") {
      # der Diamont-Operator kann durch Komma in eine Liste unterteilt sein, und es gibt extends und super
      @test = split(",", substr($sammel, 1,length($sammel)-2));
      for (my $i = 0; $i <= $#test; ++$i) {
        my $teil = $test[$i];
        $bis = index($teil, " & "); if ($bis >= 0) { $teil = substr($teil,0,$bis); } # Interfaces hinter "& " egal
        my $bez; my $klasse;
        if (($bis = index($teil, " extends ")) >= 0) { $bez = substr($teil,0,$bis); $klasse = substr($teil,$bis+9); }
        elsif (($bis = index($teil, " super ")) >= 0) { $bez = substr($teil,0,$bis); $klasse = substr($teil,$bis+7); }
        else  { $bez = $teil; $klasse = "java.lang.Object"; }
        #print("$bez => $klasse\n");
        $zuordGR->{$bez} = $klasse;
      }

      # Diamont entfernen
      $bis = index($zeile, $sammel); die "Generics-Extract $sammel nicht mehr gefunden\n" unless $bis >= 0;
      $zeile = substr($zeile, 0,$bis).substr($zeile,$bis+length($sammel));
    }
  }

  # Template-Parameter bei Namen einbauen
  my @schl = keys(%$zuordGR);
  foreach(@schl) {
    $zeile = &generics_ersetzen($zeile, $_, $zuordGR->{$_});
  }
  # Template-Parameter <? extends ...> und <? super ...> entfernen in Parameterlisten
  $zeile = &generics_wegnehmen($zeile);
  return $zeile;
}


# fuer template-Parameter z.B. T ermittelten Datentyp eintragen z.B. java.lang.Number
sub generics_ersetzen
{
  my $zeile = $_[0]; my $schl = $_[1]; my $neu = $_[2]; my $ab = 0;

  while((my $weiter = index($zeile, $schl, $ab)) >= 0) {
    # Sicherstellen, dass links/rechts keine Namenswerte wie Buchstaben/Unterstrich/Zahlen sind; <L> und (List<L> ...)
    if ($weiter > 0) {
      my $links = substr($zeile,$weiter-1,1); if ($links =~ /[\w\d]/) { $ab = $weiter + length($schl); next; }
    }
    if ($weiter < length($zeile)-1) {
      my $rechts = substr($zeile,$weiter+1,1); if ($rechts =~ /[\w\d]/) { $ab = $weiter + length($schl); next; }
    }
    $zeile = substr($zeile, 0,$weiter).$neu.substr($zeile,$weiter+length($schl));
    $ab = $weiter + length($neu);
  }

  return $zeile;
}


# Nimmt Typspez. wie <? extends Number> raus
sub generics_wegnehmen
{
  my $zeile = $_[0]; my $ab = 0;

  while((my $weiter = index($zeile, "<? ", $ab)) >= 0) {  # <? super , <? extends
    my $offen = 1; my $geschl = 0; my $suche = $weiter + 1;
    while(1) {
      my $ende1 = index($zeile, ">", $suche); die "Ende fehlt in $zeile" unless $ende1 >= 0;
      my $ende2 = index($zeile, "<", $suche);
      if ($ende2 >= 0 && $ende2 < $ende1) {
        $offen++; $suche = $ende2 + 1;
      } else {
        $geschl++; $suche = $ende1 + 1;
      }
      if ($offen == $geschl) {
        $zeile = substr($zeile, 0,$weiter).substr($zeile,$suche); last; # Abbruchbed.
      }
    }
    $ab = $weiter;
  }

  return $zeile;
}


sub generics_behandeln_attribut
{
  my $zeile = $_[0]; my $zuordGR = $_[1];
  # Bsp. Attribut:   public R b;

  # nur Ersetzen aus der map
  my @schl = keys(%$zuordGR);
  foreach(@schl) {
    $zeile = &generics_ersetzen($zeile, $_, $zuordGR->{$_});
  }
  return $zeile;
}


sub generics_behandeln_klasse
{
  my $zeile = $_[0]; my $zuordGR = $_[1];
  # Bsp. Klasse: class testp.Paar<T extends java.lang.Exception, R> {

  my $von = index($zeile, "<"); my $bis = rindex($zeile, ">");
  if ($von < 0 || $bis < 0) { return $zeile; }
  my $sammel = substr($zeile, $von, $bis-$von+1);

  # der Diamont-Operator kann durch Komma in eine Liste unterteilt sein, und es gibt extends und super
  @test = split(",", substr($sammel, 1,length($sammel)-2));
  for (my $i = 0; $i <= $#test; ++$i) {
    my $teil = $test[$i];
    $bis = index($teil, " & "); if ($bis >= 0) { $teil = substr($teil,0,$bis); } # Interfaces hinter "& " egal
    my $bez; my $klasse;
    if (($bis = index($teil, " extends ")) >= 0) { $bez = substr($teil,0,$bis); $klasse = substr($teil,$bis+9); }
    elsif (($bis = index($teil, " super ")) >= 0) { $bez = substr($teil,0,$bis); $klasse = substr($teil,$bis+7); }
    else  { $bez = $teil; $klasse = "java.lang.Object"; }
    $bez = &trim($bez);
    #print("$bez => $klasse\n");
    $zuordGR->{$bez} = $klasse;
  }
  # Diamont entfernen
  $bis = index($zeile, $sammel); die "Generics-Extract $sammel nicht mehr gefunden\n" unless $bis >= 0;
  $zeile = substr($zeile, 0,$bis).substr($zeile,$bis+length($sammel));
  return $zeile;
}


sub methodeninhaltePruefen
{
  my $klasse = $_[0];
  my $methodeR = $_[1]; my $attributR = $_[2]; my $packet = ${$_[3]}; my $ober = ${$_[4]};

  # 2) Disassemblierung, Abh�ngigkeiten anzeigen (Methodenaufrufe)
  seek DAT, 0, 0;


  # Status 0 --> suche Methode, Status 1 --> schaue in Inhalt der Methode
  my $status = 0; print "  Aufrufe von Untermethoden:\n" unless !$MAUFRUFE;
  my %aufrufe = ();    # Aufrufe als hash, damit nicht mehrfach ausgegeben
  my $methodeN = ""; my $nurStatic = 0;  # hat Methode nur statische Zugriffe
  # status: 0 = suche Methode/static-Block, 1=ist in Methode
  my $titel; $exceptionTable = 0;  # Exception Table auswerten/multicatch java7
  while (<DAT>)
  {
    chomp;
    if (index($_, "Compiled from") == 0) { next; }
    if (index($_, "Exception table:") >= 0) { $exceptionTable = 1; next; }
    if ($exceptionTable && ($von = index($_, "Class ")) >= 0) {
      $neu = substr($_, $von+6); $neu = &trafo_signatur2($neu);
      &liste_gebraucht_merken2($neu, $packet, $klasse, "t"); next;
    }
    if ($status == 0 && index($_, "static {};") >= 0)
    {
      $status = 1; $titel = $_; # Sonderfall static-Block, danach Codeblock auch durchsuchen
    }
    elsif ($status == 0 && index($_, "(") >= 0 && index($_, ")") >= 0)
    {
      # Start einer Methode erkannt, da ein Klammerpaar ( ) vorhanden
      if ($methodeN ne "" && $PUT && $nurStatic == 1) { 
         print "Methode $methodeN: static m�glich\n"; } # static zu vorherg. Methode
      my $bis = index($_, "("); my $von = rindex($_, " ", $bis); $methodeN = substr($_, $von+1, $bis-$von-1);
      $titel = $_; $status = 1;
      $nurStatic = 1;  # initiale Annahme. Konstruktoren sind nie(0), static-Methoden immer static(2) 
      if (index($_, " abstract ") >= 0 || index($_, "$packet$klasse(") >= 0) { $nurStatic = 0; } # abstract,intern Konstr.
      elsif (index($_, " static ") >= 0) { $nurStatic = 2; }
    }
    elsif ($status == 1 && length($_) == 0)
    {
      # Leerzeile, fertig, Aufrufe f�r diese Methode l�schen
      foreach $key (keys %aufrufe) { delete $aufrufe{"$key"}; }
      $status = 0; $exceptionTable = 0; next;
    }
    elsif (($von = index($_, ":")) >= 0)
    {
      # ist im K�rper einer Methode
      # nachsehen, ob eine Methode der Klasse aufgerufen wird, Attribut benutzt wird, Klassen verwendet werden
      $neu = ""; $rueck = ""; $vorne = ""; $hinten = "";  # initial leeren
      $bis = index($_, "//",$von);
      if ($bis >= 0) {
        $vorne = substr($_, $von+1,$bis-$von-1); $hinten = substr($_, $bis+2);
      } else {
        $vorne = substr($_,$von+1);
      }
      #print("xxx $vorne\n");
      #print("yyy $hinten\n");
      if (index($vorne, "new") >= 0)
      {
        &speichereNew($vorne,$hinten,$packet,$klasse);
      }
      elsif (index($vorne, "invoke") >= 0)
      {
        &speichereInvoke($vorne,$hinten,$packet,$klasse,$ober,$methodeR,\%aufrufe,\$nurStatic,\$titel);
      }
      # nur als benutzt merken, wenn gelesen, schreiben ist egal !
      elsif (index($vorne, "getfield") >= 0 || index($vorne, "getstatic") >= 0 ||
             ($PUT && (index($vorne, "putfield") >= 0 || index($vorne, "putstatic") >= 0)))
      {
        if (index($hinten, 'this:$') < 0) {
          &speichereGetPut($vorne,$hinten,$packet,$klasse,$ober,$attributR,\$nurStatic);
        }
      }
    }
  }
  close DAT;

  # Abschluss einer evt. offenen static-Pr�fung
  if ($methodeN ne "" && $PUT && $nurStatic == 1) { print "Methode $methodeN: static m�glich\n";  }

  # Teil 3: Ausgabe nicht genutzer privater Attribute/Methoden zu dieser Klasse
  ausgabe_ungenutzt ("Attribute", $attributR, $packet, $klasse);
  ausgabe_ungenutzt ("Methoden", $methodeR, $packet, $klasse);

  # anonyme Klassen sollte man hier abhandeln k�nnen, da nicht von aussen zugreifbar. Zwar kann man innerere
  # Klassen auch mit private deklarieren und hat aussen dann keinen Zugriff drauf, aber der javap kann das nicht
  # erkennen, weil er die gleiche Ausgabe wie ohne Zugriffsmodifizierer generiert, aber ohne Zfm. geht der Zugriff im Package
  #if ($klasse =~ /\$[\d]+/)   # anonyme innere Klasse z.B. Testx$0
  #{
  #  print "Anonyme innere Klasse: $klasse\n"; <STDIN>;
  #}  


  if ($DEBUG) { print "Nach Schritt 2: \n"; &testausgabe(); } 

} # Ende verarbeiten


sub speichereNew()
{
  my $vorne = $_[0]; my $hinten = $_[1]; my $packet = $_[2]; my $klasse = $_[3];
  # "KlasseB kb = null;" wird nicht erkannt, weil in javap nicht angezeigt mit Klasse:     == (aconst_null   astore_1)
  # M  10: new           #4                  // class java/util/ArrayList
  # M  1: newarray       int
  # M  5: anewarray     #11                 // class java/math/BigInteger
  my $von = index($hinten, "class ");
  if ($von >= 0) { $neu = substr($hinten, $von+6); }
  elsif (($von = index($vorne ,"newarray ")) >= 0)
  {
    $neu = substr($vorne, $von+9);
  }
  else { die "Falsches Format in $vorne $hinten\n"; }
  $neu = &trim($neu);
  $neu = &trafo_signatur2($neu); 
  &liste_gebraucht_merken2($neu, $packet, $klasse, "t") unless $neu eq "";
}


sub speichereInvoke()
{
  my $vorne = $_[0]; my $hinten = $_[1];my $packet = $_[2]; 
  my $klasse = $_[3]; my $ober = $_[4]; my $methodeR = $_[5]; my $aufrufeR = $_[6]; 
  my $nurStaticR = $_[7]; my $titel = ${$_[8]};
  # M  14: invokespecial #5                  // Method java/util/ArrayList."<init>":()V
  # M  10: invokeinterface #9,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
  # M  29: invokevirtual #16                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  # M   6: invokestatic  #11                 // Method methode4:(Ljava/lang/String;)Ljava/lang/String;
  # Lambda-Ausdruecke:
  # M   40: invokedynamic #6,  0              // InvokeDynamic #0:compare:()Ljava/util/Comparator;
  # private static int lambda$testen$0(java.lang.Integer, java.lang.Integer);

  # invoke: special, virtual oder static; R�ckgabewert hinter letztem ")" nicht gebraucht
  my $von = index($hinten, "Method ");
  if ($von >= 0) { $von += 7; }
  if ($von < 0) { $von = index($hinten, "InterfaceMethod "); if ($von >= 0) { $von += 16; } }
  if ($von < 0) { $von = index($hinten, "InvokeDynamic "); if ($von >= 0) { $von += 14; } }  # lambda-Ausdruck (java-8)
  die "Falsches Format in $vorne $hinten\n" unless $von >= 0;
  my $teil = &trim(substr($hinten, $von));
  $neu = &trafo_signatur2($teil); 
  $von = index($neu, " "); die "In $neu kein Trenner" unless $von >=0;
  $rueck = substr($neu,0, $von); $neu = substr($neu, $von+1);
  # print "Methode: Neu ist $neu, Rueck ist $rueck\n"; <STDIN>;
  # Neu ist Methodensignatur, Rueck ist R�ckgabetyp ;schauen, ob in Feld, also in dieser Klasse unter Punkt 1 ermittelt
  if ( exists $methodeR->{"$neu"}) { $methodeR->{"$neu"} = 1; }
  if ($MAUFRUFE && ! exists $aufrufeR->{$neu}) # wenn noch nicht ausgeg.
  {
    # diejenigen der Java-Standardklassen auslassen:
    if ($neu !~ /^java()?\./) { 
      if ($titel ne "") { print "$titel\n"; $titel = ""; }  # TODO was macht Titel, kommt von oben !
      $aufrufeR->{"$neu"} = 0; print "+-> $neu\n";
    }
  }
  #print("!!!Typ: $rueck!!!\n");
  &liste_gebraucht_merken2($rueck, $packet, $klasse, "t") unless $rueck eq "";
  # bei invokespecial muss kein Polymorphismus beachtet werden, gilt besonders bei Konstruktoren
  my $mitSpec = (index($vorne, "invokespecial") >= 0);
  #print("!!!Liste: $neu!!!\n");
  &liste_gebraucht_merken2($neu, $packet, $klasse, "m", $mitSpec) unless $neu eq "";

  &werteStaticAus($vorne, $nurStaticR);
}


sub speichereGetPut()
{
  my $vorne = $_[0]; my $hinten = $_[1]; my $packet = $_[2]; 
  my $klasse = $_[3]; my $ober = $_[4]; my $attributR = $_[5]; my $nurStaticR = $_[6];
  # M       17: getfield      #6                  // Field liste:Ljava/util/List;
  # M       23: putfield      #7                  // Field feldA:I
  # M       27: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
  my $von = index($hinten, "Field ");
  if ($von < 0) { die "Falsches Format in $vorne $hinten\n"; }
  $von += 6;
  my $teil = &trim(substr($hinten, $von));
  $neu = &trafo_signatur2($teil);
  $von = index($neu, " "); die "In $neu kein Trenner" unless $von >=0;
  $rueck = substr($neu,0, $von); $neu = substr($neu, $von+1);
  # print "Attribut: Neu ist $neu, Rueck ist $rueck\n";

  # Neu ist Attributname, Rueck ist Attributtyp; schauen, ob in Feld, also in dieser Klasse unter Punkt 1 ermittelt
  if ( exists $attributR->{"$neu"}) { $attributR->{"$neu"} = 1; }
  &liste_gebraucht_merken2($rueck, $packet, $klasse, "t") unless $rueck eq "";
  &liste_gebraucht_merken2($neu, $packet, $klasse, "n") unless $neu eq "";

  &werteStaticAus($vorne, $nurStaticR);
}


sub werteStaticAus()
{
  my $vorne = $_[0]; my $nurStaticR = $_[1];

  if ($$nurStaticR != 1 || index($vorne, "static") >= 0) { }
  if (index($vorne, "invokevirtual") >= 0 || index($vorne, "invokespecial") >= 0  # TODO invokeinterface ?
         || index($vorne, "getfield") >= 0 || index($vorne, "putfield") >= 0)
  {
    # wenn es ein Attribut/Methode der Klasse oder Oberklasse ist, dann kein statischer Zugriff m�glich
    # mit Java8 ist hier synt. nicht erkennbar(z.B. durch Klassenangabe zu Field), ob es in dieser oder Oberklasse ist 
    $$nurStaticR = 0;
  }
}

sub trim()
{
  my $ab=0, $bis=length($_[0])-1;
  while($ab < length($_[0]) && substr($_[0],$ab,1) eq " ") { ++$ab; }
  while($bis >= 0 && substr($_[0],$bis,1) eq " ") { --$bis; }
  return $ab <= $bis ? substr($_[0],$ab,$bis-$ab+1) : "";  
}


sub swingPruefen()
{
  my $ober = $_[0]; my $zeile = $_[1];
  my $packet = $_[2]; my $klasse = $_[3]; my $name = $_[4];
  my $vonWindowListener = $_[5]; my $vonActionListener = $_[6]; my $vonKeyListener = $_[7]; my $vonMouseListener = $_[8];

  my $eintr = 0;
  if ($ober eq "javax.swing.AbstractAction" && index($zeile," actionPerformed(java.awt.event.ActionEvent)") >= 0)
  {
    $eintr = 1;
  }
  elsif ($ober eq "javax.swing.event.MouseInputAdapter" && $zeile =~ / mouse(Pressed|Dragged|Released)\(java\.awt\.event\.MouseEvent\)/ )
  {
    $eintr = 1;
  }
  elsif ($vonWindowListener && (
       index($name, "windowClosing") >= 0 || index($name, "windowOpened") >= 0 ||  
       index($name, "windowClosed") >= 0 || index($name, "windowIconified") >= 0 ||  
       index($name, "windowDeiconified") >= 0 || index($name, "windowActivated") >= 0 ||
       index($name, "windowDeactivated") >= 0  ))
  {
    $eintr = 1;
  }
  elsif ($vonActionListener && index($name, "actionPerformed") >= 0)
  {
    $eintr = 1;
  }
  elsif ($vonKeyListener && (
       index($name, "keyPressed") >= 0 || index($name, "keyReleased") >= 0 ||  
       index($name, "keyTyped") >= 0))
  {
    $eintr = 1;
  }
  elsif ($vonMouseListener && (
       index($name, "mouseClicked") >= 0 || index($name, "mouseReleased") >= 0 ||  
       index($name, "mousePressed") >= 0 || index($name, "mouseEntered") >= 0 || index($name, "mouseExited") >= 0 ))
  {
    $eintr = 1;
  }
  $alleBezeichner{"$packet$klasse.$name"} += 1 unless $eintr == 0; # kein expliziter Aufruf, aber Ereignisse !
}


# gibt die Bezeichner und ihren Verwendungstyp aus
sub testausgabe()
{
  # Testausgabe:
  {
    my @llokal = sort(keys(%alleBezeichner));
    print "Beginn alle Bezeichner:\n";
    for (my $i = 0; $i <= $#llokal; ++$i) 
    { 
      my $schl = $llokal[$i];
      if ($schl =~ /^java(x)?\./ ) { next; }  # unwichtiges rausfiltern
      if (exists $primitiv{"$schl"}) { next; }
      print "$llokal[$i], $alleBezeichner{$llokal[$i]}\n";
    }
    print "Ende alle Bezeichner:\n"; <STDIN>;
  }
}


# ermittelt zu einer Methode oder in einer Liste (durch Komma getrennt) die Datentypen 
# und tr�gt diese als VERWENDET ein (=1); Feld-Klammern [] werden rausgesiebt
# Eingabevarianten: Attributtyp, Attributname, ganze Methode ohne R�ckgabewert,
# Liste von Datentypen
sub liste_gebraucht_merken2()
{
  my $von; my $eingabe = $_[0]; if ($eingabe eq "") { return; }
  my $bis; my $packet = $_[1]; my $klasse = $_[2]; my $modus = $_[3]; 
  my $mitSpec = ($#_ >= 4 && $_[4] ne "" ? $_[4] : 0);  # invokespecial-Modus optional
  #print "Aufruf: $eingabe\n"; <STDIN>;
  die "undefinierter Modus\n" unless ($modus eq "m" || $modus eq "t" || $modus eq "n");

  # ist es ein Methodenaufruf, dann erst ganze Methode eintragen, und dann 
  # nur noch mit der Liste der Parameter weiterarbeiten
  if (($von = index($eingabe, "(")) >= 0 && ($bis = index($eingabe, ")")) >= 0)
  {
    $teil = substr($eingabe, 0, $bis+1); # Name der Metode + Parameter
    &gebraucht_suchen($teil, $packet, $klasse, "n", $mitSpec);

    if ($von+1 == $bis) { return; }  # fertig, keine Parameter
    $eingabe = substr($eingabe, $von+1, $bis-$von-1); # nur mit Parameterliste
    # print "F�r Methode $_[0] weiter mit $eingabe\n";
  }

  $von = 0; $bis = length($eingabe)-1;  

  # verarbeite das weiter als ggf. durch Komma getrennte Liste von Typen
  do
  {
    if (substr($eingabe,$von,1) eq " ") { ++$von; } # bei Parametern in Methoden, dann modus=t
    my $weiter = index($eingabe, ",", $von);
    if ($weiter < 0 || $weiter > $bis) { $weiter = $bis+1; }
    my $teil = substr($eingabe, $von, $weiter - $von);
    my $suche = index($teil, "[");
    if ($suche >= 0) { $teil = substr($teil, 0, $suche); }
    #print "Eintragen: $teil\n"; <STDIN>;
    &gebraucht_suchen($teil, $packet, $klasse, ($modus eq "n" ? "n" : "t"), $mitSpec);

    $von = $weiter + 1;
  } while ($von <= $bis);
}


# Sucht einen Namen/Datentyp ggf. in der Verarbeitungshierarchie, sondern etwas vorliegt
# der Javap gibt die Klasse des Objekts an, z.B. C.zeige() der Klasse C, auch wenn zeige() nur in der OberKlasse B ist. Deshalb m�glichst suchen
# package ist ggf. leer. 0) heisst in Schritt 1 angelegt, 1) schon aufgerufen, 2) aufzurufen, aber in einer noch zu verarbeitenden Oberklasse
# ist der Modus ein Typ, dann wird er notfalls eingetragen; bei Namen von Attributen und Methoden wird in die Oberklasse gegangen
sub gebraucht_suchen
{
  my $eingabe = $_[0]; if ($eingabe eq "") { return; }
  my $packet = $_[1]; my $klasse = $_[2]; my $modus = $_[3]; my $mitSpec = $_[4];
  die "Modus undefiniert" unless ($modus eq "n" || $modus eq "t");

  if ($eingabe =~ /^javax?\./) { return; } # Standard-Klassen, wichtig das ^ !

  # print "Zu suchende Eingabe: $eingabe mit Packet $packet und Klasse $klasse\n";

  # 0) erstmal ermitteln, ob es Klasse hat und Package
  my $ziel = $eingabe;
  if ($modus eq "t")
  {
    if (exists $primitiv{"$eingabe"}) { return; }  # einfache Typen filtern
    # Typ hat immer Klasse dabei, ggf. kann man Package noch erg�nzen
    if ($packet ne "" && index($ziel, ".") < 0 ) { $ziel = "$packet$ziel"; } # wenn Packet fehlt, ggf. noch erg�nzen    
    $alleBezeichner{"$ziel"} = 1; return;
  }
  else
  {
    # Name eines Attributs oder Signatur einer Methode ohne R�ckgabewert
    my $wo = index($ziel, "(");
    my $temp = ($wo < 0 ? $ziel : substr($ziel, 0, $wo)); # hinter den Klammern weg, weil da Punkte in Parameter sein k�nnen
    $wo = rindex($temp, "."); my $hatKlasse = ($wo >= 0); my $hatPackage = 0;
    if ($hatKlasse)
    {
      $wo = rindex($temp, ".", $wo-1); $hatPackage = ($wo >= 0);
    }
    if (!$hatKlasse) { $ziel = "$klasse.$ziel"; } # wenn Klasse fehlt, dann ist es Methode/Attribut der aktuellen
    if ($packet ne "" && !$hatPackage)  { $ziel = "$packet$ziel"; } # wenn Packet fehlt, ggf. noch erg�nzen
  }

  {
    # bestimmte Typ der Klasse, da ja verwendet, um static-Methodenaufrufe zu erkennen
    my $wo = index($ziel, "(");
    my $temp = ($wo < 0 ? $ziel : substr($ziel, 0, $wo)); # hinter den Klammern weg, weil da Punkte in Parameter sein k�nnen
    $wo = rindex($temp, "."); die "$ziel: Eintrag ohne Klassenangaben ?\n" unless $wo >= 0;
    my $ak = substr($ziel, 0, $wo);
    &gebraucht_suchen($ak, "", "", "t", 0);
  }

  # 1) Schaue in aktueller Klasse, bei invokespecial nur dort.
  if ($mitSpec) { $alleBezeichner{"$ziel"} = 1; return; }
  if (exists $alleBezeichner{"$ziel"} && ($mitSpec || $alleBezeichner{"$ziel"} <= 1)) { $alleBezeichner{"$ziel"} = 1; return; }
  if (exists $alleBezeichner{"$ziel"} && $alleBezeichner{"$ziel"} == 2) { return; } # schon Eintrag, sp�ter nachsehen !


  # 2) In den Oberklassen, sofern schon verarbeitet, suchen und pr�fen
  do
  {
    # Klasse abtrennen und schauen, ob es dazu Oberklasse gibt, die schon eingetragen ist
    my $wo = index($ziel, "(");
    my $temp = ($wo < 0 ? $ziel : substr($ziel, 0, $wo)); # hinter den Klammern weg, weil da Punkte in Parameter sein k�nnen
    $wo = rindex($temp, "."); die "$ziel: Eintrag ohne Klassenangaben ?\n" unless $wo >= 0;
    my $ak = substr($ziel, 0, $wo);

    # print "Suche Oberklasse zu $ziel ...\n";
    if (exists $oberklassen{"$ak"})
    {
      $ober = $oberklassen{"$ak"}; # print "Oberklasse ist $ober\n";
      if ($ober eq "java.lang.Object") { return; } # Object nicht eingetragen !
      $ziel = $ober.substr($ziel, $wo) ; # print "Weiter mit $ziel\n";
      # Anm.: hier sollte nichts mehr (Klasse, Package) erg�nzt werden m�ssen !
      # hier schauen, ob Eintr�ge da sind !
      if (exists $alleBezeichner{"$ziel"} && $alleBezeichner{"$ziel"} <= 1) { $alleBezeichner{"$ziel"} = 1; return; }
      if (exists $alleBezeichner{"$ziel"} && $alleBezeichner{"$ziel"} == 2) { return; } # schon Eintrag, sp�ter nachsehen !
    }
    else
    {
      # Oberklasse noch nicht verarbeitet, keine n�here Pr�fung, deshalb 2 und nicht 1; Terminierung bei Object ?
      $alleBezeichner{"$ziel"} = 2; return; # nicht in dieser Klasse selbst, aber einer Oberklasse
    }
  }
  while (1);

}


# gibt privates ungebrauchtes aus (Methode, Attribute) oder alle global bisher nicht gebrauchten; bietet Eclipse ja auch an
sub ausgabe_ungenutzt
{
  my $bez = $_[0];
  my $zuordR = undef; my $packet; my $klasse;
  if ($bez eq "Attribute" || $bez eq "Methoden")
  {
    $zuordR = $_[1]; $packet = $_[2]; $klasse = $_[3];
  }
  elsif ($bez eq "GLOBAL")
  {
    $zuordR = \%alleBezeichner;
  }
  die "Ung�ltiger Aufrufmodus $bez\n" unless defined($zuordR);

  $gef = 0;
  my @namen = sort(keys(%$zuordR));
  my @enumTmp = keys(%enums);
  foreach my $nam (@namen)
  {
    # my $nam = $namen[$i];
    if ($zuordR->{$nam} == 2) { print "Zu $nam noch Sonderstatus 2 enthalten\n"; }
    if ($zuordR->{$nam} == 0 && index($nam, "\"<init>\"()") < 0)  # Standardkonstruktoren raus
    {
      my $ignore = 0;
      # Sonderfall: 2 Methoden von Enum noch ausnehmen
      if (index($nam, ".valueOf(java.lang.String)") >= 0 ||
          index($nam, ".values()") >= 0)
      {
        foreach my $tmp (@enumTmp)
        {
          if (index($nam, $tmp) == 0) { $ignore = 1; last; }
        }
      }
      
      if ($ignore) {}
      elsif ($bez eq "GLOBAL")
      {
        if (index($nam, ".main(") < 0) # main ausnehmen
        { 
          if ($ohneGetterSetter && ($nam =~ /.get[\w]+\(/ || $nam =~ /.set[\w]+\(/)) {} # todo zu Attribut ?
          else {
            if (!$gef) { print "\n Ungebraucht global:\n"; $gef = 1; } print "-> $nam\n";
          }
        }
      }
      else
      {
        if (!$gef) { print "\n  Ungebrauchte private $bez:\n"; $gef = 1; }
        print "-> $nam\n";
      }
    }
    if ($bez ne "GLOBAL")
    {
      # Austragen der privaten Bezeichner aus allenBezeichnern, da fuer GLOBAL dann nicht behandelt
      if (! exists $alleBezeichner{"$packet$klasse.$nam"}) { die "$nam nicht gefunden"; }
      else { delete $alleBezeichner{"$packet$klasse.$nam"}; }
    }
  }
}


# ermittelt noch den endg�ltigen Status von polymorphen Attributen und Methoden (Status 2), welche
# zum Zeitpunkt der Verarbeitung noch nicht aufgel�st werden konnten. Kann eine �nderung festgestellt werden,
# wird der Wert in der Oberklasse auf 1 gesetzt, und der auf 0 zur�ck.
sub polymorphismus_abschliessen
{
  my @namen = sort(keys(%alleBezeichner));
  foreach my $ziel (@namen)
  {
    if (! defined $alleBezeichner{"$ziel"}) { next; }  # zu tun ! wie kommen solche undef. Bezeichner rein ?

    if ($alleBezeichner{"$ziel"} == 2)
    {
      my $fertig = 0;   if ($DEBUG) { print "Noch zu analysieren: $ziel\n"; }
      # 2) In den Oberklassensuchen und pr�fen
      while (!$fertig)
      {
        if (exists $alleBezeichner{$ziel}) { delete $alleBezeichner{$ziel}; }

        # Klasse abtrennen und schauen, ob es dazu Oberklasse gibt, die schon eingetragen ist
        my $ursprung = $ziel;
        my $wo = index($ziel, "(");
        my $temp = ($wo < 0 ? $ziel : substr($ziel, 0, $wo)); # hinter den Klammern weg, weil da Punkte in Parameter sein k�nnen
        $wo = rindex($temp, "."); die "$ziel: Eintrag ohne Klassenangaben ?\n" unless $wo >= 0;
        my $ak = substr($ziel, 0, $wo);

        # print "Suche Oberklasse zu $ziel ...\n";
        if (exists $oberklassen{$ak})
        {
          $ober = $oberklassen{$ak}; # print "Oberklasse ist $ober\n";
          $ziel = $ober.substr($ziel, $wo) ; # Oberklasse + Methoden/Attributname
          # hier schauen, ob Eintr�ge da sind !
          if ($ober eq "java.lang.Object") { $fertig = 1; } # Object nicht eingetragen !
          elsif (!exists $alleBezeichner{"$ziel"} || $alleBezeichner{"$ziel"} == 2)  {}  # hier nichts
          elsif ($alleBezeichner{"$ziel"} <= 1) { $alleBezeichner{"$ziel"} = 1; $fertig = 1; } # gefunden
        }
        else
        {
          $fertig = 1; # Oberklasse nicht eingetragen
        }
      }
    }
  }
}



# wandelt die javap-Signatur (aus dem javap -c - Code) in eine "normale" Signatur um.
#  //Method KlasseA."<init>":()V
#  //Method java/math/BigInteger.add:(Ljava/math/BigInteger;)Ljava/math/BigInteger;
# //Field java/math/BigInteger.ONE:Ljava/math/BigInteger;
# //Field KlasseA.wertU:Ljava/lang/String;
# //Method KlasseA.setzeP:(ILjava/lang/String;D)V
# //InvokeDynamic #0:compare:()Ljava/util/Comparator;
sub trafo_signatur2
{
  # zu transformieren sind Parameter in Methoden, R�ckgabewerte und Attributwerte
  #  Lx; ist Referenz, I int, J long, [I int [], [J long []

  my $tModus = 0; my $ak = $_[0]; my $neu = ""; my $anz; my $feld_ausg = ""; my $trenner="";  #initial leer
  $ak =~ s/\//\./g;  # / durch . ersetzen, ggf. mehrfach
  if (substr($ak,0,1) eq "\"" && substr($ak,length($ak)-1,1) eq "\"") {
    $ak = substr($ak,1,length($ak)-2);  # umhuellende Anf'zeichen weg
  }
  if (substr($ak,0,1) eq "#") {
   my $von = index($ak, ":"); die "Lambda ? $ak" unless $von >= 0; $ak = substr($ak, $von+1); # Lamba-Ausdruck
  }
  for (my $i = 0; $i < length($ak); ++$i)
  {
    my $zeichen = substr($ak, $i, 1);
    if ($tModus && $zeichen eq "<") { $tModus = 0; $neu = $neu.$zeichen; }   # //Method "[LVersuch;".clone:()Ljava/lang/Object;  , aber nicht bei ."<init"
    elsif ($zeichen eq "\"") { $tModus = !$tModus; $neu = $neu.$zeichen; } # Anm.: bei <init> sollte man die Anf. �bernehmen !
    elsif ($zeichen eq "(" || $zeichen eq ":") { $tModus = 1; $neu = $neu.$zeichen; $trenner = ""; }  # danach TModus, auch bei R�ckgabewert
    elsif ($zeichen eq ")") { $neu = $neu.$zeichen; $trenner = ""; } # TModus danach weiter !
    elsif (!$tModus) { $neu = $neu.$zeichen; }  # unveraendert
    else
    {
      if ($zeichen eq "L")
      {
        my $weiter = index($ak, ";", $i); die ";??" unless $weiter >= 0;
        $neu = $neu.$trenner.substr($ak, $i+1, $weiter-$i-1);
        if ($feld_ausg ne "") { $neu = $neu.$feld_ausg; $feld_ausg = ""; }
        $i = $weiter; $trenner = ", ";
      }
      elsif ($zeichen eq "[")
      {
        my $weiter; # jede Dimension erzeugt ein [, ein 3D-Feld also "[[["
        for ($anz = 1; ; ++$anz)
        {
          $weiter = substr($ak, $i+$anz,1); if ($weiter ne "[") { last; }
        }
        $i += $anz -1; $feld_ausg = ("[]" x $anz); # merken, da nach Typ ausgegeben !
      }
      elsif ((my $trafo = &primitiv_wandeln($zeichen)) ne "") { 
        $neu = $neu.$trenner.$trafo; $trenner = ", ";
        if ($feld_ausg ne "") { $neu = $neu.$feld_ausg; $feld_ausg = ""; }
      }
      else { die "Trafo-Signatur: Unbekanntes $zeichen in $ak an $i\n"; }
    }
  }

  # setze den Datentyp oder den R�ckgabewert nach vorne  
  $erg = index($neu, ")");
  if ($erg >= 0) 
  { 
    $neu = substr($neu, $erg+1)." ".substr($neu, 0, $erg+1);
    $neu =~ s/://;  # DP noch raus
  }
  else
  {
    $erg = index($neu, ":");
    if ($erg >= 0) { $neu = substr($neu, $erg+1)." ".substr($neu, 0, $erg); }
  }

  # print "Trafo: $ak --> $neu\n"; <STDIN>;  # diese Zeile zur Fehlerausgabe !
  return $neu;
} # ende trafo


# wandelt primitiven code IJBSFDZCV in die Java-Typen um z.B. int, long etc.
sub primitiv_wandeln
{
  if ($_[0] eq "I") { return "int"; }
  elsif ($_[0] eq "J") { return "long"; }
  elsif ($_[0] eq "B") { return "byte"; }
  elsif ($_[0] eq "S") { return "short"; }
  elsif ($_[0] eq "F") { return "float"; }
  elsif ($_[0] eq "D") { return "double"; }
  elsif ($_[0] eq "Z") { return "boolean"; }
  elsif ($_[0] eq "C") { return "char"; }
  elsif ($_[0] eq "V") { return "void"; }
  else { return ""; }  # zeigt Fehler an
} # ende primitiv



