All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sta.cts.XMLScanner Maven / Gradle / Ivy

Go to download

Tool to convert CSV and XLS to XML, to transform XML and to convert XML to CSV, HTML, other text files, PDF etc., useful as command line tool and integrated in other projects.

There is a newer version: 3.119
Show newest version

package com.sta.cts;

import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedInputStream;

import java.util.Date;
import java.util.Hashtable;

import com.sta.mlogger.MLogger;

/**
 * 

Name: XMLScanner

*

Description: * Stellt die Funktionalit?t "Lesen einer XML-Datei" als Scanner im * Compilerbau-technischen Sinne zur Verf?gung. Durch Ableitung von Scanner * wird ein One-Token-Lookahead realisiert. Eine Zeichen-Decodierung wird * automatisch durchgef?hrt, falls die Init-Methoden mit Dateinamen oder * InputStream verwendet werden. Unterst?tzt werden 8-Bit-Encodings, * insbesondere ISO-8859-1 und UTF-8. Derzeit nicht unterst?tzt werden * 16-Bit-Encodings wie UTF-16. *

*

Copyright: Copyright (c) 2001-2004, 2011-2014, 2016-2019

*

Company: >StA-Soft<

* @author StA * @version 1.0 */ public class XMLScanner extends Scanner { /** * Operations-Level f?r XML-Bl?tter. */ protected int myLevel = 0; /** * Aktuell zu verwendende XML-Blatt-Hash-Tabelle. */ protected LeafHashtable myLHT = null; /** * Entity-Aufl?sung, falls gew?nscht. */ private Hashtable myEntities = null; //=========================================================================== /** * Standard-Constructor. */ public XMLScanner() { } //=========================================================================== /** * Aufzul?sende Entities vorgeben. * @param pEntities aufzul?sende Entities */ public void setEntities(Hashtable pEntities) { myEntities = pEntities; } /** * Zeichen vor dem erstern "<" ?berlesen, insbesondere f?r UTF-8 gedacht. * (Nur falls n?tig?) */ protected void preOverread() { try { char ch = getChar(); while ((ch != 0x00) && (ch != '<')) { ch = getChar(); } ungetChar(ch); } catch (IOException e) { } } /** * XML-Header (PI "xml") ?berlesen. * Das erfolgt in init(is, enc) ab sofort (30.04.2014) immer automatisch, * also praktisch immer dann, wenn der XMLScanner mit einem Stream * initialisiert wird, was wiederum auch dann erfolgt, wenn der XMLScanner * mit einem Dateinamen initialisiert wird. * * Diese Methode hier muss nur dann aufgerufen werden, wenn der XMLScanner * mit einem Reader initialisiert wird, weil dann das ?berlesen nicht * automatisch erfolgt. Der Aufruf st?rt jedoch im Stream-Fall nicht. * * @return Encoding, falls vorhanden, sonst null */ public String initXS() { String encoding = null; // Kopfzeile, falls vorhanden, ?berlesen Object obj = getToken(); if (obj instanceof XMLTag) { XMLTag t = (XMLTag) obj; if (t.isPI() && t.isOpen() && t.isClose() && t.getName().equals("xml")) { // overread encoding = t.getAttr("encoding"); } else { ungetToken(obj); } } // Anmerkungen: Strings sollten hier nicht auftreten und falls doch, dann // stimmt die XML-Struktur nicht. Und null-Werte zur?ckzulegen macht keinen // Sinn. Daher gen?gt obige einfachere Variante. /* boolean unget = true; Object obj = getToken(); if (obj instanceof XMLTag) { XMLTag t = (XMLTag) obj; if (t.isPI() && t.isOpen() && t.isClose() && t.getName().equals("xml")) { unget = false; } } if (unget) { ungetToken(obj); } */ return encoding; } /** * ?berschreibt die Methode init(is, enc) der Klasse Scanner. Hier wird das * Encoding, falls nicht vorgegeben (enc=null), aus dem Stream gelesen (erste * PI - Processing Instruction in einer XML-Datei) und der (XML-)Scanner mit * dem korrekten Encoding initialisiert. * @param is InputStream * @param enc Encoding (null: aus Stream ermitteln) * @throws IOException im Fehlerfall */ @Override public void init(InputStream is, String enc) throws IOException { // MLogger.deb("MarkSupported: " + is.markSupported()); if (!is.markSupported()) { is = new BufferedInputStream(is); // MLogger.deb("MarkSupported: " + is.markSupported()); } if ((enc == null) && is.markSupported()) { boolean overread = false; // is.mark(8192); is.mark(16384); super.init(is, enc); // !!!Mit "enc", damit kein Zyklus entsteht!!! preOverread(); Object obj = getToken(); if (obj instanceof XMLTag) { XMLTag t = (XMLTag) obj; if (t.isPI() && t.isOpen() && t.isClose() && t.getName().equals("xml")) { enc = t.getAttr("encoding"); overread = true; } } is.reset(); super.init(is, enc); preOverread(); if (overread) { getToken(); } } else { super.init(is, enc); boolean unget = true; Object obj = getToken(); if (obj instanceof XMLTag) { XMLTag t = (XMLTag) obj; if (t.isPI() && t.isOpen() && t.isClose() && t.getName().equals("xml")) { unget = false; } } if (unget) { ungetToken(obj); } } } /** * Initialisierung mittels einer Hash-Tabelle. Schl?ssel ist der Name der * Einstellung, Wert ist der Wert der Einstellung. * @param pHT die Hash-Tabelle mit Einstellungen und Werten (beides Strings) */ @Override public void initH(Hashtable pHT) { // preOverread(); // siehe: init(is, enc) } //--------------------------------------------------------------------------- /** * ?berlie?t Spaces incl. #$09, #$0d, #$0a. Bei anderen Zeichen erfolgt ein * Abbruch. Das entscheidende Zeichen wird gelesen und zur?ckgeliefert. * @return letztes und entscheidendes Zeichen * @throws IOException falls ein Lesefehler auftritt */ protected char overreadSpaces() throws IOException { char ch; do { ch = getChar(); } while ((ch == ' ') || (ch == 0x09) || (ch == 0x0d) || (ch == 0x0a)); return ch; } /** * Namen scannen und zur?ckliefern. Erlaubt sind Ziffern, Buchstaben und * folgende Sonderzeichen: _ - : . * Konnte kein Name gelesen werden, wird ein Leer-String geliefert. * @return Name als String * @throws IOException falls ein Lesefehler auftritt */ protected String scanNameNS() throws IOException { char ch; StringBuilder sb = new StringBuilder(); ch = overreadSpaces(); while (((ch >= '0') && (ch <= '9')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')) || ((ch == '_') || (ch == '-') || (ch == ':') || (ch == '.')) ) { sb.append(ch); ch = getChar(); } ungetChar(ch); return sb.toString(); } /** * ?berlesen aller Zeichen bis Ch1 oder Dateiende erreicht. Das entscheidende * Zeichen wird zur?ckgelegt. * @param pCh1 das Zeichen, bis zu dem alle anderen Zeichen ?berlesen werden * sollen. * @throws IOException falls ein Lesefehler auftritt */ protected void overread(char pCh1) throws IOException { char ch; ch = getChar(); while ((ch != pCh1) && (ch != 0x00)) { ch = getChar(); } ungetChar(ch); } /** * Routine speziell zum ?berlesen von XML-Kommentaren. * @param pCh1 das Zeichen, bis zu dessen doppeltem Auftreten alle anderen * Zeichen ?berlesen werden sollen. * @throws IOException falls ein Lesefehler auftritt */ protected void overread2(char pCh1) throws IOException { char ch; do { ch = getChar(); while ((ch != pCh1) && (ch != 0x00)) { ch = getChar(); } ch = getChar(); } while (ch != pCh1); // ungetChar(ch); } /** * Konvertiert Entities im Parameter-String in die entsprechenden Zeichen. * @param s Parameter-String mit Entities * @return String mit aufgel?sten Entities * @throws IOException im Fehlerfall */ protected String convert(String s) throws IOException { if (s == null) { return null; } StringBuilder sb = new StringBuilder(); int len = s.length(); for (int i = 0; i < len; i++) { char ch = s.charAt(i); if (ch == '&') { int j = s.indexOf(';', i); if (j < 0) { throw new IOException("Missing ';' for entity (" + s.substring(i) + ")."); } if (j == i) { throw new IOException("Missing entity name for (" + s.substring(i) + ")."); } String s1 = s.substring(i + 1, j); // MLogger.deb("Entity = |" + s1 + "|"); i = j; if (s1.equals("amp")) { sb.append('&'); } else if (s1.equals("lt")) { sb.append('<'); } else if (s1.equals("gt")) { sb.append('>'); } else if (s1.equals("quot")) { sb.append('"'); } else if (s1.equals("apos")) { sb.append('\''); } else if ((s1.length() > 0) && (s1.charAt(0) == '#')) { int k; if ((s1.length() > 1) && (s1.charAt(1) == 'x')) { try { k = Integer.parseInt(s1.substring(2), 16); } catch (NumberFormatException e) { throw new IOException("Hex value expected."); } } else { try { k = Integer.parseInt(s1.substring(1)); } catch (NumberFormatException e) { throw new IOException("Hex value expected."); } } sb.append((char) k); } else { Integer code = myEntities != null ? myEntities.get(s1) : null; if (code != null) { sb.append((char) code.intValue()); } else { String sx = "Invalid entity name (&" + s1 + ";)."; MLogger.err(sx); throw new IOException(sx); } } } else { sb.append(ch); } } return sb.toString(); } /** * Inhalt scannen. Es werden (vorerst) alle Zeichen bis '<' bzw. Dateiende * (excl.) zu einem String zusammengefa?t. Dieser wird zur?ckgeliefert. * Das entscheidende Zeichen wird zur?ckgelegt. * Zuk?nftig besteht die M?glichkeit, eine Liste von Strings zu liefern, in * der jede Text-Zeile als ein separater String enthalten ist (Vector of * Strings). * @return normalerweise/bisher ein String, der den Inhalt enth?lt. * @throws IOException falls ein Lesefehler auftritt */ protected Object scanContent() throws IOException { char ch; StringBuilder sb = new StringBuilder(); ch = getChar(); while ((ch != 0x00) && (ch != '<')) { sb.append(ch); ch = getChar(); } ungetChar(ch); return convert(sb.toString().trim()); // new: convert (11.08.2003) } /** * Ein Attribut scannen und im Tag eintragen. F?hrende Spaces werden nicht * ?berlesen. * @param pTag das aktuelle XML-Tag * @return true, falls alles Ok. (Name, Eq, Wert in Hochkommata) * @throws IOException falls ein Lesefehler auftritt */ protected boolean scanAttr(XMLTag pTag) throws IOException { char ch; boolean b = false; StringBuilder sb = new StringBuilder(); String n = scanNameNS(); if (n.length() != 0) { ch = overreadSpaces(); if (ch == '=') { ch = overreadSpaces(); if ((ch == '"') || (ch == '\'')) { char ch1 = getChar(); while ((ch1 != ch) && (ch1 != 0x00)) { sb.append(ch1); ch1 = getChar(); } } else { while ((ch != ' ') && (ch != 0x00) && (ch != 0x0d) && (ch != 0x0a) && (ch != '>')) { sb.append(ch); ch = getChar(); } ungetChar(ch); } b = true; pTag.setAttr(n, convert(sb.toString())); // new: convert (11.08.2003) } } return b; } /** * Scannen eines XML-Tags. Das '<' sollte bereits verarbeitet sein, * weitere f?hrende Spaces werden ?berlesen. * @return das XML-Tag oder null (Fehler) * @throws IOException - falls ein Fehler beim Lesen auftritt oder ein * logischer Fehler im Aufbau der XML-Datei vorliegt. */ protected XMLTag scanTag() throws IOException { char ch; XMLTag tag = null; ch = overreadSpaces(); if (ch == '!') { ch = getChar(); if (ch == '-') { ch = getChar(); if (ch == '-') { overread2('-'); ch = getChar(); if (ch != '>') { throw new IOException("Missing '>'."); } } else { ungetChar(ch); overread('>'); ch = getChar(); // '>' selbst } } else { ungetChar(ch); overread('>'); ch = getChar(); } } else if (ch == '?') { tag = new XMLTag(); tag.setPI(); tag.setOpen(); // ungetChar(ch); // tag.setName(scanName()); tag.setNameNS(scanNameNS()); ch = overreadSpaces(); boolean ok = true; while ((ch != 0x00) && (ch != '?') && (ch != '>') && ok) { ungetChar(ch); ok = scanAttr(tag); ch = getChar(); } if (ch == '?') { tag.setClose(); ch = overreadSpaces(); } if (ch != '>') { throw new IOException("ScanTag: Missing '>' (2)."); } } else { tag = new XMLTag(); if (ch == '/') { tag.setClose(); // tag.setName(scanName()); tag.setNameNS(scanNameNS()); ch = overreadSpaces(); if (ch != '>') { throw new IOException("ScanTag: Missing '>' (1)."); } } else { tag.setOpen(); ungetChar(ch); // tag.setName(scanName()); tag.setNameNS(scanNameNS()); ch = overreadSpaces(); boolean ok = true; while ((ch != 0x00) && (ch != '/') && (ch != '>') && ok) { ungetChar(ch); ok = scanAttr(tag); ch = getChar(); } if (ch == '/') { tag.setClose(); ch = overreadSpaces(); } if (ch != '>') { throw new IOException("ScanTag: Missing '>' (2)."); } } } return tag; } /** * Liest ein neues Token entsprechend den Konventionen von * Scanner.getNewToken. * @return ein Token */ @Override public Object getNewToken() { char ch; boolean ex = false; Object token = null; try { while (!ex) { ch = overreadSpaces(); switch (ch) { case 0x00: { ex = true; break; } case '<': { token = scanTag(); ex = (token != null); break; } default: { ungetChar(ch); token = scanContent(); ex = true; } } } } catch (IOException e) { } return token; } /** * Start-Tag eines Blatts lesen. * @param pName der geforderte Name des Blatts. * @return gelesenes Blatt-Tag als XMLTag */ public XMLTag getLeafStart(String pName) { Object token; XMLTag tag = null; token = getToken(); if (token != null) { if (token instanceof XMLTag) { tag = (XMLTag) token; if (tag.isOpen() && ((pName == null) || (tag.getName().equals(pName)))) { if (tag.isClose()) { /* tag.resOpen(); ungetToken(tag); */ // Hier m??te eigentlich ein neues schlie?endes XMLTag mit gleichem // Namen erstellt und dieses zur?ckgelegt werden. Danach sollte // tag.resClose() aufgerufen werden. XMLTag t = new XMLTag(); t.setName(tag.getName()); t.setNameSpace(tag.getNameSpace()); t.setClose(); ungetToken(t); tag.resClose(); } } else { ungetToken(token); tag = null; } } else { ungetToken(token); } } return tag; } /** * Lesen des Inhalts eines XML-Blatts. * @return Inhalt als String * @throws Exception beim Auftreten eines Lesefehlers */ public String getLeafContent() throws Exception { Object token; String strg; token = getToken(); if (token == null) { throw new Exception("XMLScanner.getLeafContent: XML-Error."); } if (token instanceof String) { strg = (String) token; } else { ungetToken(token); strg = ""; } return strg; } /** * End-Tag eines XML-Blatts lesen. * @param pName der geforderte Name des XML-Blatts * @return XML-Blatt-Tag als XMLTag * @throws Exception falls ein Lesefehler auftritt, ebenso im Falle von * XML-Struktur-Fehlern */ public XMLTag getLeafEnd(String pName) throws Exception { Object token; XMLTag tag = null; token = getToken(); if (token == null) { throw new Exception("XMLScanner.getLeafEnd: XML-Error."); } if (token instanceof XMLTag) { tag = (XMLTag) token; if (!tag.isOpen() && tag.isClose()) { if ((pName != null) && (!tag.getName().equals(pName))) { ungetToken(token); tag = null; } } else { ungetToken(token); tag = null; // throw new Exception("XMLScanner.getLeafEnd: XML-Error."); } } else { ungetToken(token); } return tag; } /** * Liest ein Blatt eines XML-Baums. Im wesentlichen wird ein Open-Tag mit dem * angegebenen Namen erwartet, danach ein Wert (Inhalt, Content) und * schlie?lich ein Close-Tag mit dem gleichen Namen wie beim Open-Tag. * Ergebnis ist der gelesene Wert. Dieser kann = "" sein. Leer-Tags sind * erlaubt. * @param pName - der Name des betreffenden Tags * @return Wert (Inhalt, Content) des Tags, im Fehlerfall (falsches Open-Tag) * wird null geliefert. * @throws Exception falls ein Lesefehler auftritt oder das Close-Tag * fehlerhaft ist. */ public String getLeaf(String pName) throws Exception { XMLTag tag; String strg; tag = getLeafStart(pName); if (tag == null) { return null; } strg = getLeafContent(); if (strg == null) { throw new Exception("Error (XMLScanner.getLeaf): Value or " + " expected."); } tag = getLeafEnd(pName); if (tag == null) { throw new Exception("Error (XMLScanner.getLeaf): " + " expected."); } return strg; } /** * String-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als String * @throws Exception siehe getLeaf */ public String getLeafString(String pName) throws Exception { return getLeaf(pName); } /** * Integer-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Integer * @throws Exception siehe getLeaf */ public Integer getLeafInt(String pName) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Int(s); } /** * Long-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Long * @throws Exception siehe getLeaf */ public Long getLeafLong(String pName) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Long(s); } /** * Float-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Float * @throws Exception siehe getLeaf */ public Float getLeafFloat(String pName) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Float(s); } /** * Double-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Double * @throws Exception siehe getLeaf */ public Double getLeafDouble(String pName) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Double(s); } /** * Date-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @param pMask optionale Maske * @return Inhalt als Date (nur Datum) * @throws Exception siehe getLeaf */ public Date getLeafDate(String pName, String pMask) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Date(s, pMask); } /** * Date-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Date (nur Datum) * @throws Exception siehe getLeaf */ public Date getLeafDate(String pName) throws Exception { return getLeafDate(pName, null); } /** * Time-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @param pMask optionale Maske * @return Inhalt als Date (nur Zeitangabe) * @throws Exception siehe getLeaf */ public Date getLeafTime(String pName, String pMask) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Time(s, pMask); } /** * Time-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Date (nur Zeitangabe) * @throws Exception siehe getLeaf */ public Date getLeafTime(String pName) throws Exception { return getLeafTime(pName, null); } /** * DateTime-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @param pMask optionale Maske * @return Inhalt als Date (Datum und Zeitangabe) * @throws Exception siehe getLeaf */ public Date getLeafDateTime(String pName, String pMask) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2DateTime(s, pMask); } /** * DateTime-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Date (Datum und Zeitangabe) * @throws Exception siehe getLeaf */ public Date getLeafDateTime(String pName) throws Exception { return getLeafDateTime(pName, null); } /** * Boolean-XML-Blatt lesen. * @param pName der Name des XML-Blatt-Tags * @return Inhalt als Boolean * @throws Exception siehe getLeaf */ public Boolean getLeafBool(String pName) throws Exception { String s = getLeaf(pName); return UniTypeConv.convString2Bool(s); } /** * myLevel erh?hen. */ public void incLevel() { myLevel++; } /** * myLevel verringern. */ public void decLevel() { myLevel--; } /** * Ermittlung der LeafHashtable, um z. B. zu pr?fen, ob diese nach Laden * eines TO's auch wirklich leer ist. * @return die LHT */ public LeafHashtable getLHT() { return myLHT; } /** * Spezielle Hashtabelle aus XML-Bl?ttern erzeugen. * In der Hashtabelle liegen unter den XML-Blatt(-Tag)-Namen (String) als * Schl?ssel der Wert des jeweiligen Blatts (ebenfalls als String). * @return diese Hashtabelle * @throws Exception falls ein Lesefehler auftritt, ebenso im Falle von * XML-Struktur-Fehlern */ public LeafHashtable getLeafs() throws Exception { if (myLevel != 0) { return myLHT; } myLHT = new LeafHashtable(); do { Object token = getToken(); if (!(token instanceof XMLTag)) { ungetToken(token); return myLHT; } XMLTag tag = (XMLTag) token; if (!tag.isOpen()) { ungetToken(tag); return myLHT; } String name = tag.getName(); StringBuilder sb = new StringBuilder(); if (!tag.isClose()) { token = getToken(); if (token instanceof String) { sb.append((String) token); } else { ungetToken(token); } token = getToken(); if (!(token instanceof XMLTag)) { throw new Exception("XML-Error 1 (leaf opening tag: " + name + " / missing leaf closing tag, found: " + (token != null ? token.toString() : "null") + ")"); } tag = (XMLTag) token; if (tag.isOpen()) { throw new Exception("XML-Error 2 (leaf opening tag: " + name + " / missing leaf closing tag, found new opening tag: " + tag.getName() + ")"); } if (!tag.isClose()) { throw new Exception("XML-Error 3 (leaf opening tag: " + name + " / found invalid tag: " + tag.getName() + ")"); } if (!tag.getName().equals(name)) { throw new Exception("XML-Error 4 (leaf opening tag: " + name + " / found invalid leaf closing tag: " + tag.getName() + ")"); } } myLHT.put(name, sb.toString()); } while (true); } /** * Pr?fen, ob die XML-Leaf-Hash-Tabelle (LHT) leer ist, * falls nicht: Meldungen ausgeben. * @param lht die LHT * @param text Text f?r die Meldung. */ public static void checkLHT(LeafHashtable lht, String text) { if ((lht != null) && !lht.isEmpty()) { lht.forEach((key, value) -> MLogger.wrn("LHT (" + text + ") not empty: " + "<" + key + ">" + value + "")); /* Enumeration e = lht.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); String val = (String) lht.get(key); MLogger.wrn("LHT (" + text + ") not empty: " + "<" + key + ">" + val + ""); } */ MLogger.wrn("Please check XML-Source-File with DTD or XSD!"); } } /** * Pr?fen, ob die XML-Leaf-Hash-Tabelle (LHT) leer ist, * falls nicht: Meldungen ausgeben, * Darf nur nach getLeafs() verwendet werden, bezieht sich auf das letzte * getLeafs(). * @param text Text f?r die Meldung. */ public void checkLHT(String text) { checkLHT(myLHT, text); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy