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

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


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, 2021

*

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; } @Override public void init(InputStream is, String enc) throws IOException { if (!is.markSupported()) { is = new BufferedInputStream(is); } 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); } } } @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); 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; } @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 - 2025 Weber Informatics LLC | Privacy Policy