# XML-Transformation mit Fortschrittsanzeige



## guni (2. Mrz 2010)

Hallo,

ich habe eine XML-Datei, die ich über einen Saxon-Parser in ein anderes XML-Format umwandle.
Der Umwandlungsprozess kann sehr lange dauern, da die Datei ca. 10 Megabyte groß ist.
Gibt es irgendeine Möglichkeit, dass ich mir den Fortschritt der Transformation anzeigen lassen kann?!

mfg, guni


----------



## Noctarius (2. Mrz 2010)

Nicht, dass ich wüsste... Was du möglicherweise machen kannst wäre eine eigene Ableitung eines Readers oder Inputstreams und die Länge der Datei gegen die gelesenen Zeichen als Prozentwert verfügbar machen (bzw als Observerable ein notify mit Länge und aktueller Position absetzen).


----------



## guni (2. Mrz 2010)

hmm ... wow. klingt nicht unkompliziert.
Aber gut - Ich nehme die Herausforderung an.
Ärmel hoch ;-)

mfg, guni


----------



## guni (2. Mrz 2010)

Also - meine Versuche scheitern kläglich:

bis jetzt sieht mein Programm so aus:

```
public class MyStreamSource extends Observable implements Result
{
	private FileInputStream fos;
	
	public MyStreamSource(String filename) throws FileNotFoundException {
		this.fos = new FileInputStream(filename);
	}

	@Override
	public String getSystemId() {
		return null;
	}

	@Override
	public void setSystemId(String systemId) {
	}
}
```

dann schreibe ich eine Klasse die Observer implementiert.
In ihrer update-Methode soll dann eine Prozentanzeige stehen oder sowas.
Aber: Wo kann ich festlegen, wann sich tatsächlich etwas geändert hat?! 
Sagen wir, die einzulesende Datei hat 100 MB -> nach jedem eingelesenen Megabyte möchte ich die setChanged-Methode aufrufen damit mein Observer ein weiteres Prozent Fortschritt schreibt ...
Mein Problem ist aber, dass der Stream ja nicht von mir gelesen wird sondern von der transform Methode meines Transformers. Ich bin also wieder darauf angewiesen, dass ich IRGENDWIE ein Event abfange dass mir sagt, wenn eine Datei bis zu einem bestimmten Prozentsatz eingelesen ist.
Irgendwie hab ich das Gefühl, dass dieser Ansatz mein Problem gar nicht lösen kann ... HILFE!!!


mfg, guni


----------



## SlaterB (2. Mrz 2010)

du sollst an den Transformer nicht einen  FileInputStream übergeben, sonden ein Objekt einer eigenen neuen Klasse, die das Interface InputStream implementiert,
dann wird bei deiner Klasse read() usw. aufgerufen (genau schauen, welche Methoden benötigt werden),
die Daten holt sich deine Klasse aus dem FileInputStream und reicht sie nur weiter, aber nebenbei kannst du auch anderes machen, z.B. die Anzahl der gelesenen Bytes zählen und Ereignisse absenden, 

alternativ das gleiche Spiel mit dem OutputStream am anderen Ende


----------



## Noctarius (2. Mrz 2010)

Genau Slater hat's etwas umfassender ausgedrückt  Das meinte ich *g*


----------



## guni (3. Mrz 2010)

> das InputStream-Interface???


Ich finde kein InputStream-Interface. 
Nur ein Objekt.
Und das kann ich nicht extenden weil sonst kann ich ja Observable nicht mehr extenden!
mfg, guni


----------



## SlaterB (3. Mrz 2010)

tja, dann eben von InputStream erben bzw. was auch immer der Saxon-Parser als Eingabe verlangt,
von Observable musst du nicht erben,

du kannst ein Observable als Klassenattribut vorherhalten, siehe z.B.
http://www.java-forum.org/allgemeine-java-themen/96852-observer-vs-listener.html#post616015
bzw. mehr als 20 Zeilen sinnvollen Code enthält diese Klasse eh nicht, das kann man auch kopieren,
eine Liste der Observer, eine add-Methode und eine notify-Methode die die Liste der Observer durchläuft,
Spezialcode wie setChanged() brauchst du doch sicher nicht


oder gar zwei verschiedene Klassen schreiben,
der eigene InputStream ruft nur an einem Objekt einer anderen eigenen Klasse update(derzeitiger Stand) auf oder ähnlich


----------



## guni (3. Mrz 2010)

Hallo,
jetzt steh ich leider schon wieder an.
Also - ich hab mir jetzt einen ObservableInputStream gebaut - er sieht so aus:

```
public class ObservableInputStream extends FileInputStream implements Observable
{
	private ArrayList<Observer> observers = new ArrayList<Observer>();
	private float bytesread;
	private float filesize;
	private float percent;
	
	public ObservableInputStream(File file) throws FileNotFoundException {
		super(file);
		this.bytesread = 0;
		this.filesize = (float)file.length();
	}

	@Override
	public int read() throws IOException 
	{
		int b = super.read();
		percent = (++bytesread)/filesize*100;
		stateChanged();
		return b;
	}

	@Override
	public float getState() 
	{
		return percent;
	}

	@Override
	public void notifyObservers() {
		for (Observer o : observers)
			o.refresh();
	}

	@Override
	public void register(Observer o) {
		observers.add(o);
	}

	@Override
	public void stateChanged() {
		notifyObservers();
	}

	@Override
	public void unregister(Observer o) {
		observers.remove(o);
	}
}
```
das Ding funktioniert recht gut - ich kann Observer registrieren, die mir dann anzeigen, wieviel Prozent der Datei bereits eingelesen ist.
... nur - WIE verbinde ich das jetzt mit dem Transformer?!
... meine eigentliche Anforderung ist ja nicht, dass ich wissen will wieviel bereits eingelesen ist, sondern wieviel bereits umgewandlet ist!!
Und die Umwandlung scheint der Transformer in ein Result zu schreiben.
Aber: das Result-Interface hat keine Methode, die ich observieren könnte :-(
und woher weiß ich überhaupt, welche Methode der Transformer intern aufruft / welche Methode ich überschreiben muss?!

mfg, guni

PS.: ach ja - noch was:
ich habe probiert, meinen InputStream in einen BufferedReader zu laden:

```
BufferedReader br = new BufferedReader(new InputStreamReader(new ObservableInputStream(new File("test.txt"))));
```
mein Problem ist jetzt, dass wenn ich eine while ((line=br.readLine())!=null) - Schleife baue, dann wird die Datei zwar schön eingelesen; mein Observer bleibt aber ruhig. greift readLine nicht auf read zurück? Wieso kann ich die Datei nicht Observieren, wenn ich sie über einen Reader einlese?
Als Alternative zum BufferedReader hab ich mir das Einlesen der Datei dann wie folgt aufgebaut:

```
protected final void read(ObservableInputStream ois)
	{		
		InputStreamObserver iso = new InputStreamObserver();
		ois.register(iso);	
		int b = 0;
		// wie bau ich die eol hier noch ein?!
		String eol = System.getProperty("line.separator");
		try {
			while ((b = ois.read()) != -1) {
				currentLine += b;
				if (b == '\n') {
					t.convertLine((++linenumber), currentLine, previousLine);
					previousLine = currentLine;
					currentLine = "";
				}
			}
		} catch (IOException err) {
			err.printStackTrace();
		} catch (Exception err) {
			err.printStackTrace();
		}
	}
```
mein Problem hier ist jetzt: wie erkenne ich, wann eine Zeile zu Ende ist?!


----------



## Noctarius (3. Mrz 2010)

Der Sax-Parser nutzt Streaming für das Einlesen. Ergo dürfte der eingelesene Wert ziemlich nah am Transform-Status liegen. Eine andere Variante würde mir auch nicht einfallen.


----------



## guni (3. Mrz 2010)

@Noctarius:
was bedeutet das?!
Das Einzige was ich der Transform-Methode als Stream übergebe ist doch eigentlich mein XSL.
Und von dem weiß ich eh, dass es nicht lange zum Einlesen braucht.

Meinen Transformer erzeuge ich so:

```
private static Transformer createTransformer(StreamSource xslt, HashMap<String, String> parameters, HashMap<String, String> outputprops) throws TransformerConfigurationException
	{
		System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
		SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
		Transformer t = tf.newTransformer();
		Iterator<String> i = null;
		
		if (xslt != null) {
			t = tf.newTransformer(xslt);
		}
		
		if (parameters != null) {
			i = parameters.keySet().iterator(); 
			while (i.hasNext()) {
				String key = i.next();
				t.setParameter(key, parameters.get(key));
			}
		}
		
		if (outputprops != null) {
			i = outputprops.keySet().iterator();
			while (i.hasNext()) {
				String key = i.next();
				t.setOutputProperty(key, outputprops.get(key));
			}
		}
		
		return t;
	}
```

und meine Transform-Methoden sehen dann so aus:

```
public static void toCsv(Element e, StreamSource xslt, HashMap<String, String> parameters, HashMap<String, String> outputprops, StreamResult r) throws TransformerException
	{
		Transformer t = createTransformer(xslt, parameters, outputprops);
		t.transform(new DOMSource(e), r);
	}
	
	public static void toXml(Element e, StreamSource xslt, HashMap<String, String> parameters, HashMap<String, String> outputprops, DOMResult r) throws TransformerException
	{
		Transformer t = createTransformer(xslt, parameters, outputprops);
		t.transform(new DOMSource(e), r);
	}
```
... also hab ich doch zum Zeitpunkt des Transform-Aufrufes gar keinen Stream, oder?!


----------



## SlaterB (3. Mrz 2010)

> also hab ich doch zum Zeitpunkt des Transform-Aufrufes gar keinen Stream, oder?! 
vs
> Transform-Methode als Stream übergebe ist doch eigentlich mein XSL

was soll das bedeuten, du widersprichst dich selber, worum geht es aktuell?
natürlich kann der Eingabe-Stream in einem Stück ausgelesen werden, 
wenn es so ist hast du Pech mit dem Loggen, das kann man nicht genau wissen,

edit: und die Eingabe besteht aus zwei Teilen, das Template sowie die aktuellen Daten, die zu verarbeiten sind,
das Template wird gewiss vorher komplett eingelesen, es geht primär um die Nutzdaten, die übersetzt werden,
bei dir anscheinend 'Element e', also ein Datenmodell in Java? dann wäre das die Stelle, dort verfolgen, wann wo welches Child aufgerufen wird usw.,


und auch sollte klar sein, dass es irgendeine Art von Ausgabe gibt, was macht dein Programm denn? 
schreibt es von Zauberhand irgendwo eine Datei, oder musst du nur einen Dateinamen übergeben 
oder erhälst du doch einen AusgabeStream, den du z.B. in einen FileOutputStream weiterleiten musst?
dann wäre das die zweite Möglichkeit zu loggen,
wiederum ist natürlich nicht ausgeschlossen, dass die Blackbox Transformer alle lange Arbeit fertig stellt, bevor auch nur ein Byte gesendet wird,
im kleinen Rahmen ist das auch gar nicht so unwahrscheinlich, wenn aber MB an Eingaben und Ausgaben durchzusetzen sind, dann sollte jede höhere API eigentlich mit Streams Schritt für Schritt arbeiten

---------

> mein Observer bleibt aber ruhig. greift readLine nicht auf read zurück?

du musst sämliche Methoden der Oberklasse überschreiben und zunächst nur testweise loggen, um herauszufinden, welche benutzt wird,
read() zum Lesen eines einzelnen Bytes ist gewiss eher uninteressant, read(byte[]) schon viel wichtiger


----------



## Noctarius (3. Mrz 2010)

Du kannst ja statt DomSource auch andere Sources nutzen


----------



## guni (3. Mrz 2010)

hmm ... denkst du, dass die Sources der Teil ist, wo der Transformer so langsam ist?!
ich fürchte, dass ich das Result loggen muss, oder?!
Und da komm ich nicht an den Stream, oder?!

... nochmal zurück zu meinem oben beschriebenen Problem:
ich habe folgenden Code:

```
protected final void read(ObservableInputStream ois)
    {       
        InputStreamObserver iso = new InputStreamObserver();
        ois.register(iso);  
        int b = 0;
        // wie bau ich die eol hier noch ein?!
        String eol = System.getProperty("line.separator");
        try {
            while ((b = ois.read()) != -1) {
                currentLine += b;
                if (b == '\n') {
                    t.convertLine((++linenumber), currentLine, previousLine);
                    previousLine = currentLine;
                    currentLine = "";
                }
            }
        } catch (IOException err) {
            err.printStackTrace();
        } catch (Exception err) {
            err.printStackTrace();
        }
    }
```
aber wie erkenne ich, ob die Line tatsächlich aus ist?!
bzw. wie "sage" ich, dass currentLine nur die Zeile ohne dem newline-character sein soll?
so wie beim buffered reader eben ;-)

mfg, guni


----------



## SlaterB (3. Mrz 2010)

> Und da komm ich nicht an den Stream, oder?!

hängt davon ab was los ist, ich kann meine Texte nur wiederholen


> und auch sollte klar sein, dass es irgendeine Art von Ausgabe gibt, was macht dein Programm denn?
> schreibt es von Zauberhand irgendwo eine Datei, oder musst du nur einen Dateinamen übergeben
> oder erhälst du doch einen AusgabeStream, den du z.B. in einen FileOutputStream weiterleiten musst?
> dann wäre das die zweite Möglichkeit zu loggen,


du lieferst keine Infos..

-------

> ich habe folgenden Code:

worum gehts? der Sinn des Codes oder eine Verbindung zu irgendetwas vorherigem erschließt sich mir nicht


----------



## guni (3. Mrz 2010)

Also, mal ganz von vorne:

* Ich habe eine CSV-Datei.
* Ich lese sie ein.
* dann rufe ich in meiner Schleife für jede Zeile eine convertLine-Methode auf
* die convertLine macht einen split und erstellt dann für jede Zeile ein Element <LINE COL1="VAL1" COL2="" .../>
* am Ende meines Einleseprozesses habe ich also ein DOM-Objekt.
* meine CSV-Datei hat 4MB; die XML-Datei ist ca. doppelt so groß.
* nun habe ich - dank eurer Hilfe - einen eigenen InputStream, der pro eingelesenen 10% der Gesamtgröße der Datei ausgibt, wieviel Prozent bereits eingelesen wurden.

Das funktioniert soweit.
Mein Problem dabei ist:
* da ich in meinem InputStream die read-Methode überschreibe, muss ich auch mit der read-Methode einlesen!
* die convertLine braucht aber eine Zeile (so, wie sie von der Methode BufferedReader.readLine zurückgegeben wird)
* nun bin ich mir nicht sicher, wie ich in meiner Einleseprozedur erkennen kann, wann eine Zeile zu Ende ist (siehe Code oben)

SO. DAS war mein erstes Problem.

Mein zweites Problem ist folgendes:
* ich transformiere das soeben erstellte DOM-Objekt in einem nächsten Schritt per XSLT in ein anderes DOM-Objekt.
* Dieser Vorgang dauert seeehr lange.
* nun möchte ich - genau wie beim Einlesen der CSV Datei - einen Ausgabe machen anhand derer erkennbar ist, welchen Fortschritt die Umwandlung des Transformierens bereits hat.
* Transformer.transform bekommt von mir eine DOMSource und ein Result (entweder StreamResult oder DOMResult)
* nun weiß ich nicht, was ich da in welcher Klasse überschreiben muss, um den Prozessfortschritt des Transformprozesses zu bekommen. Ich habe die saxon-Klassen bereits dekompiliert; aber ich finde den für mich relevanten Methodenaufruf nicht.

mfg, guni


----------



## SlaterB (3. Mrz 2010)

guni hat gesagt.:


> Also, mal ganz von vorne:
> 
> * Ich habe eine CSV-Datei.
> * Ich lese sie ein.
> ...


aha, soweit aber nicht besonders sinnvoll, das Einlesen der CSV-Datei nach DOM-Objekt musst du überhaupt nicht loggen, 
oder wenn dann in deiner Schleife mit convertLine, was immer das bedeutet,

in jedem Fall brauchst du hier keinen überschriebenen InputStream!, 
denn wenn du sowieso in deiner Schleife Code ausführst kannst du da doch alles machen was du willst,
ein überschriebener Stream wäre nur dann nötig wenn ein Transformer 5 Min. was macht ohne das von dir auch nur eine Zeile Code drankommt,




> Mein zweites Problem ist folgendes:
> * ich transformiere das soeben erstellte DOM-Objekt in einem nächsten Schritt per XSLT in ein anderes DOM-Objekt.
> * Dieser Vorgang dauert seeehr lange.
> * nun möchte ich - genau wie beim Einlesen der CSV Datei - einen Ausgabe machen anhand derer erkennbar ist, welchen Fortschritt die Umwandlung des Transformierens bereits hat.
> ...


ab hier wirds interessant, hier hast du eine Blackbox, die für sich lange lange arbeitet, 
dabei (primär) nur auf zwei Weisen mit ihrer Umgebung interagiert: 
zum einen hat sie eine Eingabe, die du übergibst, die DOMSource bzw. den DOM-Baum dahinter, keinen Stream,
über diese Eingabe hast du recht viel Kontrolle, wenn auch vielleicht nicht direkt, dann immer durch überschriebene Klassen,
alle public-Methoden überschreiben und schauen wann sie aufgerufen werden

die zweite Interaktion findet mit der Ausgabe Result statt, oder dessen Stream oder was auch immer,
wie am Ende eine Datei daraus wird hast du auch nach zweimaligen Fragen bisher nicht geantwortet,


hier ein Dummy-Beispiel, worum es geht:

```
public class Test
{
    static final long START = System.currentTimeMillis();

    public static void main(String[] args)
    {
        Eingabe e = new Eingabe();
        Ausgabe a = new Ausgabe();
        test(1, e, a);

        LoggerEingabe le = new LoggerEingabe(e);
        test(2, le, a);
    }

    static void test(int x, Eingabe eingabe, Ausgabe ausgabe)
    {
        System.out.println("\nstarte BlackBox " + x);
        new BlackBox(eingabe, ausgabe);
        System.out.println("fertig BlackBox " + x + 
          "     , Zeit: " + (System.currentTimeMillis() - START));
    }
}

class BlackBox
{
    public BlackBox(Eingabe eingabe, Ausgabe ausgabe)
    {
        for (int i = 0; i < eingabe.length(); i++)
        {
            String st = eingabe.read();
            st = st + " transformed";
            ausgabe.write(st);
            try
            {
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

class Eingabe
{
    public int length()
    {
        return 10;
    }

    public String read()
    {
        return "test";
    }
}

class Ausgabe
{

    public void write(String st)
    {
    }
}

class LoggerEingabe
    extends Eingabe
{
    private Eingabe e;
    private int count;

    public LoggerEingabe(Eingabe e)
    {
        this.e = e;
    }

    public String read()
    {
        count++;
        System.out.println("Eingabe gelesen, " + count + "/" + 
           e.length() + ", Zeit: " + (System.currentTimeMillis() - Test.START));
        return e.read();
    }
}
```
Ausgabe:

```
starte BlackBox 1
fertig BlackBox 1     , Zeit: 5015

starte BlackBox 2
Eingabe gelesen, 1/10, Zeit: 5015
Eingabe gelesen, 2/10, Zeit: 5515
Eingabe gelesen, 3/10, Zeit: 6015
Eingabe gelesen, 4/10, Zeit: 6515
Eingabe gelesen, 5/10, Zeit: 7015
Eingabe gelesen, 6/10, Zeit: 7515
Eingabe gelesen, 7/10, Zeit: 8015
Eingabe gelesen, 8/10, Zeit: 8515
Eingabe gelesen, 9/10, Zeit: 9015
Eingabe gelesen, 10/10, Zeit: 9515
fertig BlackBox 2     , Zeit: 10015
```
die BlackBox ruft in beiden Fällen irgendwas von der Eingabe auf,
im zweiten Durchlauf ist diese Klasse aber überschrieben und so kann alles komfortabel verfolgt werden,
im echten Programm ist es bisschen komplizierter, aber das gleiche Prinzip,
auch Ausgabe könnte überschrieben werden


----------



## Noctarius (3. Mrz 2010)

Dann erstellst du einen BufferedReader vom InputStreamReader von deinem InputStream und schon hast du die richtige Methode read() benutzt.

Aber das du eine CVS liest war mir bis eben neu, und wieso wandelst du die CVS auf diese Weise um? Oo Wäre es da nicht sinnvoller eine CvsSource oder sowas zu bauen, die das alles in einem Schritt macht?


----------



## guni (3. Mrz 2010)

@SlaterB


> in jedem Fall brauchst du hier keinen überschriebenen InputStream!,
> denn wenn du sowieso in deiner Schleife Code ausführst kannst du da doch alles machen was du willst,
> ein überschriebener Stream wäre nur dann nötig wenn ein Transformer 5 Min. was macht ohne das von dir auch nur eine Zeile Code drankommt,


stimmt. deswegen war ja auch mein ursprünglicher Ansatz eine eigene Result-Klasse zu schreiben ...
Dass mir der Stream für den Transformer eigentlich gar nix bringt hab ich erst später erkannt 



> die zweite Interaktion findet mit der Ausgabe Result statt, oder dessen Stream oder was auch immer, wie am Ende eine Datei daraus wird hast du auch nach zweimaligen Fragen bisher nicht geantwortet,


Ganz am Ende ist es natürlich eine Datei.
Aber der Transformer bekommt in erster Line mal ein DOM-Result übergeben das wieder in eine DOM-Node schreibt.



> die BlackBox ruft in beiden Fällen irgendwas von der Eingabe auf,
> im zweiten Durchlauf ist diese Klasse aber überschrieben und so kann alles komfortabel verfolgt werden,
> im echten Programm ist es bisschen komplizierter, aber das gleiche Prinzip,
> auch Ausgabe könnte überschrieben werden


danke für den Source.
hmm ... das sieht ja nach einer Menge Arbeit aus.
die Methoden die ich überschreibe muss ich also erraten, oder?!

@Noctarius


> Dann erstellst du einen BufferedReader vom InputStreamReader von deinem InputStream und schon hast du die richtige Methode read() benutzt.


ja. das habe ich eigentlich auch gedacht:

```
BufferedReader br = new BufferedReader(new InputStreamReader(new ObservableInputStream(new File("test.txt"))));
```
... aber es scheint nicht zu funktionieren.
Wahrscheinlich muss ich - wie SlaterB schon vorher gesagt hat eine andere read-Methode überschreiben weil der InputStreamReader gar nicht auf diese Methode zugreift :-(



> Aber das du eine CVS liest war mir bis eben neu, und wieso wandelst du die CVS auf diese Weise um? Oo Wäre es da nicht sinnvoller eine CvsSource oder sowas zu bauen, die das alles in einem Schritt macht?


hmm ... die CSV-Dateien die ich einlese haben sehr verschiedene Formate.
also hab ich mir gedacht ich schreib einfach eine Klasse, die allgemein eine CSV in ein XML umwandelt. Der Vorteil wäre für mich, dass ich die XML dann über XSLT umwandeln kann ...


----------



## SlaterB (3. Mrz 2010)

guni hat gesagt.:


> die Methoden die ich überschreibe muss ich also erraten, oder?!


grundsätzlich schon, erstmal wäre aber wichtig, sich zu entscheiden, was überhaupt überschrieben wird,
wenn z.B. die Node-Klassen im Dom-Baum dann gibts ja auch nicht viele wichtige, getChild() usw.

wenn Dom einen fertigen Baum liefert, wäre es allerdings schon extrem aufwendig, den durch einen komplett eigenen Baum zu ersetzen, wenig aussichtreich,
schaue lieber in Richtung Result und dort vielleicht eine Art OutputStream,

die Zählung der Nicht-Lieferung von Infos dazu geht weiter, bisher 3x,
'Ganz am Ende ist es natürlich eine Datei.' zählt nicht  , Code wäre nützlich



guni hat gesagt.:


> Wahrscheinlich muss ich - wie SlaterB schon vorher gesagt hat eine andere read-Methode überschreiben weil der InputStreamReader gar nicht auf diese Methode zugreift :-(


mit InputStream musst derzeit gar nix machen, für eigenes Einlesen sowieo nicht,
für den Transformer auch nicht denn der verwendet keinen InputStream (auf Nutzdaten, Template ist egal)..


----------



## Noctarius (3. Mrz 2010)

guni hat gesagt.:


> die Methoden die ich überschreibe muss ich also erraten, oder?!



Wie wäre es mit Source ansehen? ;-)

InputStreamReader:

```
/**
     * Reads a single character.
     *
     * @return The character read, or -1 if the end of the stream has been
     *         reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    public int read() throws IOException {
        return sd.read();
    }

    /**
     * Reads characters into a portion of an array.
     *
     * @param      cbuf     Destination buffer
     * @param      offset   Offset at which to start storing characters
     * @param      length   Maximum number of characters to read
     *
     * @return     The number of characters read, or -1 if the end of the 
     *             stream has been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    public int read(char cbuf[], int offset, int length) throws IOException {
	return sd.read(cbuf, offset, length);
    }
```

BufferedReader

```
/**
     * Reads a line of text.  A line is considered to be terminated by any one
     * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
     * followed immediately by a linefeed.
     *
     * @param      ignoreLF  If true, the next '\n' will be skipped
     *
     * @return     A String containing the contents of the line, not including
     *             any line-termination characters, or null if the end of the
     *             stream has been reached
     * 
     * @see        java.io.LineNumberReader#readLine()
     *
     * @exception  IOException  If an I/O error occurs
     */
    String readLine(boolean ignoreLF) throws IOException {
	StringBuffer s = null;
	int startChar;

        synchronized (lock) {
            ensureOpen();
	    boolean omitLF = ignoreLF || skipLF;

	bufferLoop:
	    for (;;) {

		if (nextChar >= nChars)
		    fill();
		if (nextChar >= nChars) { /* EOF */
		    if (s != null && s.length() > 0)
			return s.toString();
		    else
			return null;
		}
		boolean eol = false;
		char c = 0;
		int i;

                /* Skip a leftover '\n', if necessary */
		if (omitLF && (cb[nextChar] == '\n')) 
                    nextChar++;
		skipLF = false;
		omitLF = false;

	    charLoop:
		for (i = nextChar; i < nChars; i++) {
		    c = cb[i];
		    if ((c == '\n') || (c == '\r')) {
			eol = true;
			break charLoop;
		    }
		}

		startChar = nextChar;
		nextChar = i;

		if (eol) {
		    String str;
		    if (s == null) {
			str = new String(cb, startChar, i - startChar);
		    } else {
			s.append(cb, startChar, i - startChar);
			str = s.toString();
		    }
		    nextChar++;
		    if (c == '\r') {
			skipLF = true;
		    }
		    return str;
		}
		
		if (s == null) 
		    s = new StringBuffer(defaultExpectedLineLength);
		s.append(cb, startChar, i - startChar);
	    }
        }
    }
```


----------



## guni (4. Mrz 2010)

@Noctarius:

1. BufferedReader
a. wow. die ReadLine Methode ruft ja gar nirgends ein read auf ... d.h. wenn ich einen InputStream überschreibe und ihn dann über einen InpurStreamReader in einen BufferedReader einlese habe ich eigentlich gar nichts davon?!
b. was bedeuten die "Schleifenbezeichnungen". Ich habe soetwas noch nie gesehen ... Ist das so eine Art Label? dient das nur der Leserlichkeit oder hat das einen anderen Sinn auch noch?
c. Wo hast du denn den Source her? Hast du dir den selbst dekompiliert? Ich verwende den JavaDecompiler, aber er gibt mir weit nicht so schönen code zurück.

2. Source ansehen
mein Problem ist: ich will ja nicht die Methoden erraten, die der BufferedReader aufruft, sondern die Methoden, die von der transform-Methode (Saxon-Transformer) aufgerufen wird.
Der Source davon ist endlos und ich habe keine Ahnung, welche Methoden interessant sein könnten :-(

@Slater-B:
Code? Das wird schwierig. das Ganze ist in einige Klassen aufgeteilt.
Welchen Code-Ausschnitt möchtest du denn gern sehen?
wie gesagt: 
* ich erzeuge ein Element e und meinetwegen ein Element x
* dann rufe ich die Methode transformer.transform(DOMSource(e), DOMResult(x)) auf.
* jetzt habe ich das transformierte xml in einer Node x stehen.

in einem anderen Context recht ähnlich:
* ich erzeuge ein Element e und irgendeinen OutputStream o (je nachdem ob gespeichert oder ausgegeben wird natürlich unterschiedlich)
* dann rufe ich die Methode transformer.transform(DOMSource(e), StreamSource(o)) auf.
* jetzt habe ich das transformierte xml an meinen angegebenen outputstream weitergeleitet.

mfg, guni


----------



## Noctarius (4. Mrz 2010)

guni hat gesagt.:


> a. wow. die ReadLine Methode ruft ja gar nirgends ein read auf ... d.h. wenn ich einen InputStream überschreibe und ihn dann über einen InpurStreamReader in einen BufferedReader einlese habe ich eigentlich gar nichts davon?!



Das bedeutet nur, dass der BufferedReader seinem Namen zum Besten gebend erst buffered und dann ausgibt  Ergo wird erst alles gelesen. Da du aber für den Source nur einen Reader brauchst, kannst du den InputStreamReader auch direkt nutzen.



guni hat gesagt.:


> b. was bedeuten die "Schleifenbezeichnungen". Ich habe soetwas noch nie gesehen ... Ist das so eine Art Label? dient das nur der



Das sind Sprungmarken für die return Statements (sowas ähnliches wie Goto in hässlichen Sprachen und in der Java Welt sehr selten genutzt



guni hat gesagt.:


> c. Wo hast du denn den Source her? Hast du dir den selbst dekompiliert? Ich verwende den JavaDecompiler, aber er gibt mir weit nicht so schönen code zurück.



Aus dem JDK. Im Installationsordner liegt eine Datei src.zip und da sind alle Java-Sources drin (ansonsten in Eclipse die Klasse einfach mal aufmachen, ist alles richtig eingestellt siehst du den Sourcecode auch).



guni hat gesagt.:


> 2. Source ansehen
> mein Problem ist: ich will ja nicht die Methoden erraten, die der BufferedReader aufruft, sondern die Methoden, die von der transform-Methode (Saxon-Transformer) aufgerufen wird.
> Der Source davon ist endlos und ich habe keine Ahnung, welche Methoden interessant sein könnten :-(



Dann schaust du dir halt die richtige Klasse als Source an


----------



## SlaterB (4. Mrz 2010)

guni hat gesagt.:


> Welchen Code-Ausschnitt möchtest du denn gern sehen?
> wie gesagt:
> [..]
> * jetzt habe ich das transformierte xml an meinen angegebenen outputstream weitergeleitet.


das reicht schon, hier ist wieder ein Stream beteiligt, dort könntest du einen eigenen Stream übegeben, bei dem du die write-Methoden überschreibst

ein DomResult, welches wieder einen Dom-Baum erzeugt ist genauso schlecht zu loggen wie der Dom-Imput,
beim Stream wäre es leichter


----------



## guni (4. Mrz 2010)

hmm ... ich brauche es in der Weiterverarbeitung aber als DOM :-(
d.h. ich müsste folgendes tun

1. einen OutputStream out erstellen
2. Dom-Objekt root in Stream in umwandlen
3. transform(in, out)
4. out wieder in Dom-Objekt umwandeln.

... ist das nicht extrem imperformant, wenn der Transformer eigentlich schon einen DomSource und ein DomResult übergeben bekommen kann?

@SlaterB:
habe ich deine Frage bzgl. dem OPutput beantwortet oder möchtest du noch irgendeinen Source sehen?

mfg, guni


----------



## SlaterB (4. Mrz 2010)

mir reichen vorerst die Informationen

> ... ist das nicht extrem imperformant, wenn der Transformer eigentlich schon einen DomSource und ein DomResult übergeben bekommen kann?

so ist es, aber es war ja auch nur ein Vorschlag, vom ersten Post an gestern wurde dir nichts weiter gesagt als dass du die Stream-Verarbeitung relativ leicht loggen könntest,
nun nach endlosen Diskussionen verrätst du nach und nach die simple Information, dass du in der Hauptverarbeitung (direktuer Input + Output für den Transformer) wenig bis gar nichts mit Streams machst,
das hatten wir halt so nicht vermutet,
mit einem Dom-Baum ist es prinzipiell ähnlich möglich zu loggen, aber evtl. auch viel schwerer, wäre genau zu analysieren, keine leichte Aufgabe


----------



## guni (4. Mrz 2010)

@SlaterB:

ja ... bei meinem Posting habe ich wohl nicht verstanden, auf welche Informationen es wirklich ankommt.
aber allein durch die Fragen die ihr stellt habe ich schon wieder einiges gelernt.

also: ich versuche nochmal kurz die Funktionalität meines Programmes zusammenzufassen - vielleicht fällt euch eine Möglichkeit ein, wie ich das ganze etwas Stream-lastiger bauen könnte ;-)

1. Zeilenweises einlesen einer CSV
2. konvertieren jeder einzelnen CSV-Zeile in ein XML-Element
3. append des aktuelle Elements an ein root-Element
--
RESULTAT: ein DOM Tree
--
4. transformieren des DOM Trees per XSLT
--
RESULTAT: ein (neuer) DOM Tree
--
5. durchlaufen/modifizieren des DOM-Trees
--
RESULTAT: noch immer ein DOM-Tree
--
6. transformieren des Trees per XSLT
--
RESULTAT: ein DOM Tree
--
7. transformieren des Trees per XSLT
--
RESULTAT: ein Stream Result im CSV-Format
--
8. Speichern oder Ausgeben des Stream Results.

soweit zu der allgemeinen Funktion meines Programmes.
Die CSV-Dateien kommen in sehr unterschiedlichen Formaten daher.
Deswegen ermöglicht mir obenstehendes Design folgendes:

* Schritt 1 - Schritt 3 meines Programmes sind für jede CSV-Datei gleich
* Schritt 4: bezieht sich auf das spezifische Format: bei Bedarf hier ein XSL angegeben
* Schritt 5: bezieht sich auf das spezifische Format
* Schritt 6: wandelt das spezifische Format in ein allgemeines XML-Format um
* Schritt 7: wieder für alle Formate gleich: da im Vorschritt ein allgemeines Format erstellt worden ist, kann hier leicht wieder in verschiedene CSV-Formate zurück umgewandelt werden.

so:
für den 1. Transform hab ich also auf jeden Fall mal einen Tree als Input - den muss ich ja aufbauen
für den 1. Transform brauche ich als Output auch wieder einen Tree - damit ich ihn durchlaufen kann
für den 2. Transform bekomme ich als Input wieder einen Tree, als Output könnte ich Streamen
für den 3. Transform könnte ich Input und Output Streamen.

... ist die Beschreibung halbswegs verständlich?
... wie könnte ich das denn umbauen, dass ich immer Streams an die transform-Methode schicke?
Dazu müsste ich ja in meinem Programm immer wieder DOM in Stream umwandeln und wieder zurück!
Das ist sehr unschön, oder?
Und wie wandle ich überhaupt von DOM in Stream um?
Ich hab das bis jetzt nur über den Transformer gemacht ...

mfg, guni


----------



## SlaterB (4. Mrz 2010)

Schritt 7 ließe ein Logging per Stream zu,
ich nehme an ansonsten sind noch Schritt 4 und 6  über Transformer? dauern die beide lange, geht es auch um diese oder um welche Schritte?

bei Dom-Bäumen würde ich ja bisher wie gesagt auch nicht an einen Eingriff wie in einen Stream denken,
mal ganz andere Richtung: ist die Dauer immer gleich, abhängig von der Größe?
kommt eine Schätzung aus Erfahrungswerten in Frage, eine separater Thread der nur vermutet dass nach x Min. alles fertig sein wird und solange eine geschätzte Prozentanzeige erhöht?


----------



## guni (4. Mrz 2010)

ich brauche das logging leider schon in schritt 4.
und ich denke, dass die Dauer von mehreren Faktoren (nicht nur von der Größe) abhängig ist.

ok ... mir leuchtet auf jeden Fall mal ein, dass DOMSource / DOMResult nicht so leicht zu loggen ist wie StreamSource / StreamResult.
Sieht so aus als wäre die vernünftigste Lösung jeden Transformationsschritt in eine Datei zwischenzuspeichern (was aber wieder einen Aufruf der transform-Methode erfordert :-() und neu einzulesen / neu zu parsen :-(

oder gibt es eine andere Möglichkeit wie ich einen DOMSource in einen InputStream / ein DOMResult in einen OutputStream umwandeln kann?

mfg, guni


----------



## Noctarius (4. Mrz 2010)

Also noch mal zum mitschreiben:
1. CVS einlesen (zeilenweise)
2. CVS in XML-DOM umwandeln (1:n Elemente?)
3. Per Transformer das XML-DOM in anderes DOM umwandeln

Und über Schritt 2 und 3 brauchst du Progress indications?


----------



## SlaterB (4. Mrz 2010)

nur Schritt 3, der allerdings mindestens zweimal stattfindet (4 und 6) in gunis Auflistung,
Schritt 7 auch, aber dann DOM in Stream


> 1. CVS einlesen (zeilenweise)
> 2. CVS in XML-DOM umwandeln (1:n Elemente?)
wären keine Probleme da das meist schnell geht und wenn dann ja auch über (gunis) eigenen Code, da kann man Zusätze einfügen wie man will


----------



## Noctarius (4. Mrz 2010)

Nee weil momentan eine Variante, die mir einfällt, wäre erstellte Elemente zählen und dem Transformer einen ContentHandler unterzuschieben der jedes startElement zählt.
Dann hätte man eine ungefähre Prozentzahl.

Umgekehrt dann eben alle Elemente zählen die aus dem Transformer fallen und alle weggeschriebenen zählen.

PS: Das hat mich darauf gebracht, dass ich auch noch einen ProgressListener Interface in meinem Parser brauche *mitnotiert hat*


----------



## SlaterB (4. Mrz 2010)

so ein ContentHandler klingt gut, 
wenn du dazu nähere Angaben machen kannst, dann mache das,
ich nehme mal an dass guni nicht mehr dazu weiß als ich und ich wüßte jetzt noch nicht was zu tun ist, frage also für guni 

edit: bei SAXResult kann man den einbauen, das ist ja auch fast ne Stream-Verarbeitung, aber bei DOM?


----------



## guni (4. Mrz 2010)

> 1. CVS einlesen (zeilenweise)
> 2. CVS in XML-DOM umwandeln (1:n Elemente?)
> 3. Per Transformer das XML-DOM in anderes DOM umwandeln
> 
> Und über Schritt 2 und 3 brauchst du Progress indications?



1. wie SlaterB schon gesagt hat: BRAUCHEN tue ich die ProcessIndication über Schritt 3 (in meiner Aufzählung wären das die Schritte 4,6 und 7.
2. Über Schritt 2/3 (deine Aufzählung) wären diese Indications eher eine Frage der Kosmetik, aber die sind nicht so langsam.
3. was meinst du mit 1:n Elemente? beim erstmaligen Einlesen ist bei mir jede Zeile genau ein Element. jede Spalte ist ein Attribut. Beim letzten Transformieren ist dann jede Zeile ein Element mit einem Kindelement für jede Spalte.



> Nee weil momentan eine Variante, die mir einfällt, wäre erstellte Elemente zählen und dem Transformer einen ContentHandler unterzuschieben der jedes startElement zählt.


Ja. An diesen Ansatz habe ich anfangs auch gedacht. Hab mich dann ein bisschen damit herumgespielt, aber ich bin irgendwie nicht wirklich weitergekommen und hatte dann am Schluß eigentlich den Eindruck, dass ich mit einem ContentHandler sowas gar nicht realisieren kann weil ich einem Transformer ja keinen Handler SETZEN kann ???:L



> ich nehme mal an dass guni nicht mehr dazu weiß als ich und ich wüßte jetzt noch nicht was zu tun ist, frage also für guni


danke SlaterB :toll: ... du hast recht. Ich bin zwar schon über die Thematik gestoßen; hab es aber irgendwie nicht ganz geschafft, dieses Konzept auf meinen Anwendungsfall runterzubrechen :bahnhof:

Ach ja: Frage an euch: ihr habt CVS geschrieben aber das Dateiformat heißt doch eigentlich CSV, oder?!

... und noch was - falls es euch interessiert: habe gerade herausgefunden, wie ich ein DOM-Objekt wieder in einen Stream umwandeln kann:
also: erste Möglichkeit war ja

```
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new DOMSource(e), new StreamResult(System.out));
```

dann habe ich noch das hier gefunden:

```
XMLSerializer s = new XMLSerializer();
s.setOutputByteStream(System.out);
s.serialize(d);
```

Methode 1 benötigt bei meinem XML ungefähr 11000 ms; Methode 2 ist auf 3000 ms. WOW! Wer hätte das gedacht. So einen gravierenden Unterschied hätte ich mir nicht vorgestellt ...


----------



## Noctarius (4. Mrz 2010)

Muss da auch noch etwas rumprobieren  So fix eben runtertippen ist nicht. Wird auch auf den Xalan selber hinauslaufen. Mit nur SAX-Api wird's vermutlich nicht gehen.


----------



## guni (4. Mrz 2010)

Hallo,
nochmal zu der Streamlösung:
ich hab mich jetzt mal wieder in Neuland begeben: Threads!
Mein Programm sieht zur Zeit so aus:

```
public static void main(String[] args) throws Exception
	{
		System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
		final Transformer t = TransformerFactory.newInstance().newTransformer(new StreamSource(new FileInputStream("conf/postparse/ktr.xsl"))); 
		final Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("output/first.xml"));		
		final PipedOutputStream pos = new PipedOutputStream();
		final PipedInputStream pis = new PipedInputStream(pos);
		
		Runnable serializeDom = new Runnable()
		{
			@Override
			public void run() {
				try {
					XMLSerializer s = new XMLSerializer();
					s.setOutputByteStream(pos);
					s.serialize(d);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		};
		
		Runnable transformStream = new Runnable()
		{
			@Override
			public void run() {
				try {
					StreamSource in = new StreamSource(pis);
					StreamResult out = new StreamResult(new FileOutputStream("output/converted.xml"));
					t.transform(in, out);
				} catch (TransformerException e) {
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
		};
		
		Thread domSerializer = new Thread(serializeDom, "Serializer");
		Thread streamTransfomer = new Thread(transformStream, "Transfomer");
		
		domSerializer.start();
		streamTransfomer.start();
	}
```
Jetzt gibt es da noch ein paar Unklarheiten meinerseits: 

1. Sobald ich das Programm ausführe, bekomme ich einen Heap-Space-Error. 
Das verstehe ich nicht. Ich dachte, Streams können "endlos" groß sein, weil das Programm sowieso gleich wieder vergisst was es schreibt/liest. Oder hab ich da was falsch verstanden am Konzept.
Oder puffert vielleicht meine StreamSource/braucht der Transformer den ganzen Puffer auf einmal?!

2. Nichts desto trotz - ich bin das Problem mal umgangen indem ich java mit -Xms512m -Xms1024m aufgerufen habe.
Dann kommt der Heap-Space-Error nicht mehr.
Dafür bekomme ich jetzt einen anderen Fehler (nur ein Auszug):

```
Error 
  java.io.IOException: Write end dead
net.sf.saxon.trans.XPathException: java.io.IOException: Write end dead
	at net.sf.saxon.event.Sender.sendSAXSource(Sender.java:423)
	at net.sf.saxon.event.Sender.send(Sender.java:182)
	at net.sf.saxon.Controller.transform(Controller.java:1691)
	at com.guni.test.crack_my_blackbox.Main$2.run(Main.java:64)
	at java.lang.Thread.run(Thread.java:619)
Caused by: java.io.IOException: Write end dead
	at java.io.PipedInputStream.read(PipedInputStream.java:294)
	at java.io.PipedInputStream.read(PipedInputStream.java:361)
	at ...
```
wieso um alles in der Welt habe ich ein WriteDeadEnd?! Sowas hatte ich noch nie! Das ganze funktioniert übrigens auch nicht, wenn ich beim StreamResult statt einem FileOutputStream einfach System.out angebe ... ;(

PS.: @Noctarius: diese Fragen sollen dich natürlich nicht von deinen Experimenten mit dem ContentHandler abhalten .gg.


----------



## guni (4. Mrz 2010)

Fehler gefunden.
Man muss die Streams closen ;-)

... LÖST ABER NOCH NICHT MEINEN HEAP-SPACE-ERROR ;(


----------

