import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;


// Einlesen einer dBase-datei
// mittels DataInputStream
// GUI ist vollstaendig

// (c), 2008 Michael Wilhelm


public class Read_dBase1 extends JFrame {
   // globale GUI-Elemente
  JTextField Editzeile;
  JTextArea Editor;

  //Frame konstruieren
  public Read_dBase1() {

    setSize(1000, 700);
    setTitle("Einlesen einer dBase-Datei");

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setGUI();
  } // create


  // setzen der GUI-Elemente, vollständig
  void setGUI() {
    JLabel Label1;
    JScrollPane Sc;
    JButton BnOk;		// Schalter definieren
    JButton BnEsc;		// Schalter definieren
    Box box1;
    Box box2;

      // North
    Label1 = new JLabel();
    Label1.setText("Datei");
    Label1.setFont( new Font("Arial", Font.BOLD,28));
    Editzeile = new JTextField();
    Editzeile.setFont( new Font("Arial", Font.BOLD,28));
    // Editzeile.setText("test_bsp1.dbf"); 
    Editzeile.setText("gewässer.dbf");

    box1 = Box.createVerticalBox();
    box1.add(  Box.createVerticalStrut(5) );
    box1.add(Label1);
    box1.add(  Box.createVerticalStrut(5) );
    box1.add(Editzeile) ;
    box1.add(  Box.createVerticalStrut(30) );
    this.getContentPane().add(box1, BorderLayout.NORTH);

      // Center
    Editor = new JTextArea();
    Editor.setFont( new Font("Arial", Font.BOLD,28));
    Editor.setText("-");
    Sc = new JScrollPane(Editor);
    getContentPane().add( Sc, BorderLayout.CENTER);

      // South
		BnOk = new JButton();  	// Schalter erzeugen
		BnOk.setText("Ok");  	// Text setzen
    BnOk.setFont( new Font("Arial", Font.BOLD,28));
		BnEsc= new JButton();  	// Schalter erzeugen
		BnEsc.setText("Esc");  	// Text setzen
    BnEsc.setFont( new Font("Arial", Font.BOLD,28));
    box2 = Box.createHorizontalBox();
    box2.add(  Box.createVerticalStrut(40) );
    box2.add(  Box.createHorizontalStrut(70) );
    box2.add(BnOk);
    box2.add(  Box.createHorizontalStrut(70) );
    box2.add(BnEsc) ;
    box2.add(  Box.createHorizontalStrut(70) );
    this.getContentPane().add(box2, BorderLayout.SOUTH);

     // setzen Event-Handler
    BnOk.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {
        BnOk_Click();
      }
    });
    BnEsc.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {
        BnEsc_Click();
      }
    });

  }



  // wird ausgelöst beim Drücken des Schalters ESC
  void BnEsc_Click() {
    System.exit(0);
  }


  public static void main(String[] args) {
    Read_dBase1 frame = new Read_dBase1();
    frame.setVisible(true);
  }

  // testet, ob die Datei vorhanden ist
  public boolean FileExists(String sFileName) {
      File f = null;
      try {
          // evnetuell kann auch f.pathSeparatorChar verwendet werden
          f = new File(sFileName);
          if (f.exists())
              return true;
          else
              return false;
      }
      catch (Exception e) {
          return false;
      }
  }  // FileExists


  // wird ausgelöst beim Drücken des Schalters Ok
  void BnOk_Click() {
    String NL = "\r\n";
    FileInputStream Fin;
    DataInputStream Din;
    String sFilename;
    dBase_Header header;
    dBase_Feld felder[];
    int i, j, k;
    int i1,i2,i3,i4;
    String sStr;
    char ch;

    sFilename = Editzeile.getText();
    if ( ! FileExists(sFilename) ) {
      Editor.setText("Datei ist nicht vorhanden");  // Editorinhalt löschen
      return;
    }
    Editor.setText("");  // Editorinhalt löschen
    try {
         Fin = new FileInputStream(sFilename);
       		// Druckausgabe
         Din = new DataInputStream(Fin);
         header = new dBase_Header();
         header.loadFromFile(Din);

         Editor.append( "dBase Version: "+header.Version + NL);
         Editor.append( "Jahr: "+header.jahr + NL);
         Editor.append( "Monat: "+header.monat + NL);
         Editor.append( "Tag: "+header.tag + NL);

         Editor.append( "AnzRecords: "+header.anzRecords + NL);
         Editor.append( "HeaderSize: "+header.headersize + NL);
         Editor.append( "RecordsSize: "+header.recordsize + NL);
         Editor.append( "AnzFelder: "+header.AnzFelder + NL);

         // Lesen der Felder
         felder = new dBase_Feld[header.AnzFelder];
         for (i=0; i<header.AnzFelder;i++) {
           felder[i] = new dBase_Feld();
           felder[i].loadFromFile(Din);
           Editor.append( "i : "+(i+1) + NL);
           Editor.append( "  Feldname:"+felder[i].FeldStr + NL);
           Editor.append( "  cTyp:"+felder[i].cTyp + NL);
           Editor.append( "  Fieldsize:"+felder[i].fieldsize + NL);
           Editor.append( "  Nachkomma:"+felder[i].nk + NL);
           Editor.append( "  Offset:"+felder[i].offset + NL);
         } // for

         i = (int) Din.readByte();  // 0D

         // Lesen der Daten pro Feld
         // Ausgabe erstmal nur als String
         // es müssten noch Klassen definiert werden, in den
         // automatisch die Attribute gespeichert werden
         // es muss auch eine automatische Umwandlung implementiert werden
         for (i=0; i<header.anzRecords;i++) {
           Editor.append( "\nDatensatz:"+(i+1) + NL);
           k = (int) Din.readByte();  // trennzeichen Blank  32 oder0x20
           for (j=0; j<header.AnzFelder;j++) {
             //Editor.append( "  Offset:"+felder[i].offset + NL);
             switch (felder[j].cTyp) {
               case 'C': // Zeichenfolge
                    sStr="";
                    for (k=0;k<felder[j].fieldsize; k++) {
                      sStr+= (char) Din.readByte();  //
                    }
                    Editor.append( "    "+sStr + NL);
                    break;
               case 'N':  // Numerisch mit oder ohne Nachkommastelle
                   sStr="";
                    for (k=0;k<felder[j].fieldsize; k++) {
                      sStr+= (char) Din.readByte();  //
                    }
                    Editor.append( "    "+sStr + NL);
                    break;
               case 'D':  // datum
                    sStr="";
                    for (k=0;k<felder[j].fieldsize; k++) {
                      sStr+= (char) Din.readByte();  //
                    }
                    Editor.append( "    "+sStr + NL);
                    break;
               case 'L':  // boolean
                    ch = (char) Din.readByte();  //
                    if ( (ch=='T') || (ch=='Y') || (ch=='J') )
                      Editor.append( "    "+"true" + NL);
                    else
                      Editor.append( "false" + NL);
                    break;
             }
           } // for
         }
    	   Din.close();
    }
    catch (IOException ee) {
        System.err.println("IOException: " + ee);
    }

  }  // BnOk_Click

}


// Klasse fuer den Header
// oeffentliche Variablen, vereinfacht
class dBase_Header {
     public int Version;  // byte
     public int jahr;  // byte
     public int monat;  // byte
     public int tag;  // byte
     public int anzRecords;   // Anzahl der Datensaetze
     public int headersize;   // WORD Headergroesse in Byte
     public int recordsize;   // Word Laenge eines einzelnen Datensatzes */
     private byte  dummy[] = new byte[20];
     public int AnzFelder;

     // liest den Header
     public void loadFromFile(DataInputStream Din) {
         byte b, b1, b2, b3, b4;
         int i1,i2;
         try {
           b = Din.readByte();  // Vorzeichenbehaftet
           Version = (int) b;
           jahr = (int) Din.readByte();  // Vorzeichenbehaftet;
           monat = (int) Din.readByte();  // Vorzeichenbehaftet;
           tag = (int) Din.readByte();  // Vorzeichenbehaftet;

           // Problem little Endian
           b1 = Din.readByte();  // Vorzeichenbehaftet
           b2 = Din.readByte();  // Vorzeichenbehaftet
           b3 = Din.readByte();  // Vorzeichenbehaftet
           b4 = Din.readByte();  // Vorzeichenbehaftet
           //anzRecords = (int)Din.readInt();  //
           anzRecords =
  (b4 << 24 ) | (b3 << 16 ) | (b2 << 8 )
          | b1;

           //b4 = Din.readByte();  // Vorzeichenbehaftet
           i1 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i1<0)  i1+=256;
           i2 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i2<0)  i2+=256;
           headersize = (i2 << 8 ) | i1;

           i1 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i1<0)  i1+=256;
           i2 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i2<0)  i2+=256;
           recordsize = (i2 << 8 ) | i1;
           for (int i=0; i<dummy.length; i++) {
             b = Din.readByte();  // Vorzeichenbehaftet
           }

           // Jeder Feldeintrag ist 32 Byte lang
           // 33 Byte sind vorweg eingetragen
           AnzFelder = (int)( (headersize-33) / 32 );

         }
         catch (IOException ee) {
            System.err.println("IOException: " + ee);
         }
     }

} // dBase_Header


// Klasse fuer einen feldeintrag, keine Daten
//   nur Definition
class dBase_Feld {
    public String FeldStr;      // Feldname   aber nur Grossbuchstaben  11 Zeichen
    public char cTyp;           //Feldtyp  C=String; N=Zahl; D=Datum; L=Boolean
    private byte  dummy1[] = new byte[4];

    public int  fieldsize;           // Feldlaenge byte
    public int  nk;             // Nachkommastellen byte
    private byte  dummy2[] = new byte[14];
    public  int offset;  // speichert den Offset jedes Feldes
                     // eventuell zum zurückschreiben

     // liest die definition eines Feldes
     public void loadFromFile(DataInputStream Din) {
         byte b, b1, b2;
         char ch;
         int i, i1,i2;
         try {
           FeldStr="";
           // offset = Din.
           for (i=0; i<11;i++) {
             ch = (char) Din.readByte();  // kein Unicode, Char
             if (ch>0) FeldStr+=ch;  // mit Zeichen Null am Ende
           }
           cTyp = (char) Din.readByte();

           for (i=0; i<dummy1.length; i++) {
             b = Din.readByte();  // Vorzeichenbehaftet
           }

           i1 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i1<0)  i1+=256;
           fieldsize = i1;


           i1 = (int) Din.readByte();  // Vorzeichenbehaftet
           if (i1<0)  i1+=256;
           nk = i1;

           for (i=0; i<dummy2.length; i++) {
             b = Din.readByte();  // Vorzeichenbehaftet
           }

         }
         catch (IOException ee) {
            System.err.println("IOException: " + ee);
         }
     }

}  // Read_dBase1


class DataInputStream2 extends DataInputStream {

  public DataInputStream2(FileInputStream fin) {
    super(fin);
  }

  public int readInt_littleEndian() throws IOException {
    short b, b1, b2, b3, b4;
    b1 = (short) super.readByte();  // Vorzeichenbehaftet
    b2 = (short) super.readByte();  // Vorzeichenbehaftet
    b3 = (short) super.readByte();  // Vorzeichenbehaftet
    b4 = (short) super.readByte();  // Vorzeichenbehaftet
    int i = (b4 << 24 ) | (b3 << 16 ) | (b2 << 8 )| b1;
    return i;
  }
  public int readShort_littleEndian() throws IOException  {
    short b, b1, b2;
    b1 = (short) super.readByte();  // Vorzeichenbehaftet
    b2 = (short) super.readByte();  // Vorzeichenbehaftet
    int i = (b2 << 8 ) | b1;
    return (int) i;
  }

}

