SuperX-Kernmodul
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

509 lines
20 KiB

package de.superx.common;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import de.memtext.baseobjects.coll.IdObjectSet;
import de.memtext.util.NumberUtils;
import de.memtext.util.XMLUtils;
import de.superx.bin.fm.EtlStarter;
import de.superx.util.SqlStringUtils;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateSequenceModel;
/**
* Template Processor for FreeMarker
*/
public class TemplateProcessor {
private Configuration cfg;
enum SqlVarType {
list,
hashlist,
hash,
hashsequence,
sicht,
sichtsequence,
string,
none
}
// wird für Lieferung ans Applet gebraucht
public SxResultSet rawFmTemplates;
protected HashMap repositoryMap = new HashMap();
protected String mandantenID = DEFAULT_MANDANTEN_ID;
protected Connection con = null;
public static final String REPOSITORY_SELECT = "select trim(both from id) as id,caption,content,gueltig_seit,gueltig_bis,comment,sort1,sort2,sort3 from sx_repository where aktiv=1";
static {
try {
freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE);
} catch (Exception e) {
System.out.println("FreeMarker warning: " + e);
}
}
public TemplateProcessor(String mandantenID, Connection con) {
this(mandantenID);
this.con = con;
}
public TemplateProcessor(String mandantenID) {
this.mandantenID = mandantenID;
Configuration myCfg = new Configuration(Configuration.VERSION_2_3_21);
myCfg.setTemplateLoader(new StringTemplateLoader());
try {
// die Formatierungseinstellung wird u.a. für sortnr gebraucht,
// sonst erscheint z.B. 1000 als 1.000
myCfg.setSetting("number_format", "###########.####");
myCfg.setSharedVariable("etl", new EtlStarter(mandantenID));
} catch (TemplateException e) {
System.out.println("FreeMarker warning: " + e);
e.printStackTrace();
}
this.cfg = myCfg;
}
public void setRepositoryMap(HashMap repository) {
this.repositoryMap = repository;
}
public void add(String id, String ts) {
((StringTemplateLoader) cfg.getTemplateLoader()).putTemplate(id, ts);
}
/**
* synchronized, damit bei Mandantenfähigkeit nicht zufällig praktisch
* gleichzeitig, 1 Thread ein Template für 17020 hinterlegt, dann ein 2. Thread
* ebenfalls für 17020 (und damit den Eintrag des ersten überschreibt) und der
* erste Thread dann fälschlicherweise den Eintrag vom 2. bekommt
* CFG.setTemplateLoader(new StringTemplateLoader(stringTemplates));
*
* trotz stringTemplates - sind Probleme aufgetaucht, wenn schnell
* hintereinander z.b. 3x mal xil_proplist als templateName kam (Makro
* eigentlich nicht sekundengenau gleichzeitig) als workaround wird zufällige
* Nummer angehängt
*
* @param templateName
* @param template
* @return
* @throws IOException
*/
protected synchronized Template buildTemplateObject(String templateName, String template) throws IOException {
Template t = null;
templateName = cleanName(templateName);
templateName += "-rnd:" + NumberUtils.getRandomInt(1000000000);
// replace all unicode whitespace chars with regular whitespace
// (those can get introduce for example when copy-pasting from html)
// TODO: Right place?
template = template.replaceAll("\\p{javaSpaceChar}", " ");
((StringTemplateLoader) cfg.getTemplateLoader()).putTemplate(templateName, template);
try {
t = cfg.getTemplate(templateName);
// damit nicht fälschlicherweise nochmal ausgelesen wird
cfg.removeTemplateFromCache(templateName);
} catch (freemarker.core.ParseException e) {
throw new RuntimeException("FreeMarker-Parsing fehlgeschlagen - " + e.getMessage()
+ "DETAILS: Die Zeilennummer bezieht sich auf folgende Vorlage\n(Masken select_stmt nach SuperX-Verarbeitung mit generateSql)\n"
+ template);
}
return t;
}
/**
* Wird vom SuperXDBServlet aufgerufen und von der anderen process Methode
*
* @param fmSql
* @param hmap
* @return
* @throws TemplateException
* @throws IOException
* @throws SQLException
*/
public String process(String templateName, String fmSql, HashMap hmap, String sqldialect)
throws TemplateException, IOException, SQLException {
StringBuffer b = new StringBuffer();
fmSql = b.toString() + fmSql;
templateName = cleanName(templateName);
hmap.put("SQLdialect", sqldialect);
hmap.putAll(repositoryMap);
if (fmSql.indexOf("<sqlvars>") > -1) {
addSqlVars(hmap, fmSql, sqldialect);
fmSql = removeSqlVars(fmSql);
}
Writer out = new StringWriter();
Template t = buildTemplateObject(templateName, fmSql);
t.process(hmap, out);
out.close();
return out.toString();
}
private synchronized void addSqlVars(HashMap hmap, String fmSql, String sqldialect)
throws SQLException, TemplateException, IOException {
int i = fmSql.indexOf("<sqlvars>");
int i2 = fmSql.indexOf("</sqlvars>");
String sqlvarsxml = fmSql.substring(i, i2 + 10);
Document doc = null;
try {
doc = XMLUtils.buildDocumentFromStringWithException(sqlvarsxml, false);
} catch (SAXException e) {
String msg = "Sqlvars konnten nicht zu XML-Dokument verarbeitet werden, Syntax checken ";
System.out.println(msg);
System.out.println(sqlvarsxml);
e.printStackTrace();
throw new SQLException(msg + "vergl. catalina.out ");
}
NodeList list = doc.getElementsByTagName("sqlvar");
for (i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
String varname = XMLUtils.getAttribValue(n, "name");
String sql = n.getTextContent();
SqlVarType type = SqlVarType.none;
if ( XMLUtils.hasAttrib(n, "type") ) {
type = SqlVarType.valueOf(XMLUtils.getAttribValue(n, "type").toLowerCase());
}
String name_intern = "";
if (type == SqlVarType.sichtsequence) {
SxResultSet roh = readFromDb(sql);
FMListe liste = new FMListe();
for (Iterator<SxResultRow> it = roh.iterator(); it.hasNext();) {
SxResultRow zeile = it.next();
String gewuenschterStand = "today";
liste.add(addSqlVarSicht(hmap, "nur_ein_dummy", (String) zeile.get(0), gewuenschterStand));
}
hmap.put(varname, liste);
} else if (type == SqlVarType.hashlist) {
List<TemplateHashModel> results = selectFromDb(sql);
hmap.put(varname, results);
} else if (type == SqlVarType.sicht) {
name_intern = XMLUtils.getAttribValue(n, "name_intern");
String gewuenschterStand = "today";
if (XMLUtils.hasAttrib(n, "stand"))
gewuenschterStand = XMLUtils.getAttribValue(n, "stand");
addSqlVarSicht(hmap, varname, name_intern, gewuenschterStand);
} else if (type == SqlVarType.list) {
SxResultSet roh = readFromDb(sql);
List<String> results = new ArrayList<String>();
for (Iterator it = roh.iterator(); it.hasNext();) {
SxResultRow zeile = (SxResultRow) it.next();
results.add( zeile.get(0).toString());
}
hmap.put(varname, results);
} else if (type==SqlVarType.string)
{
String resultstring = process("sqlvar " + varname + NumberUtils.getRandomInt(99999999), sql, hmap, sqldialect);
hmap.put(varname, resultstring);
}
else {
if (SqlStringUtils.tableExists(con, "sx_repository", mandantenID)) {
sql = "<#include \"SuperX_general\"/><#include \"SQL_lingua_franca\"/>" + sql;
}
sql = process("sqlvar " + varname + NumberUtils.getRandomInt(99999999), sql, hmap, sqldialect);
SxResultSet roh = readFromDb(sql);
FMListe liste = new FMListe();
boolean isSelectableItem = false;
Object entry = "";
int rowcount = roh.size();
for (Iterator it = roh.iterator(); it.hasNext();) {
SxResultRow zeile = (SxResultRow) it.next();
if (type == SqlVarType.hash || type == SqlVarType.hashsequence) {
SxHash h = new SxHash();
for (int j = 0; j < zeile.size(); j++) {
String key = roh.getColumnName(j).toLowerCase();
Object value = zeile.get(j);
h.put(key, value);
}
if (rowcount == 1) {
entry = h;
}
liste.add(h);
} else {
entry = zeile.get(0);
if (entry == null)
entry = "";
String nam = "";
if (zeile.size() > 1) {
nam = SqlStringUtils.getValueAsString(zeile.get(1));
isSelectableItem = true;
}
SelectableItem item = new SelectableItem(entry, nam);
if (zeile.size() > 2) {
item.setStrukturInfo(zeile.get(2));
}
if (zeile.size() > 3) {
item.setStrukturInfo(zeile.get(3));
}
liste.add(new SelectableItemNode(item));
}
}
if (type == SqlVarType.hashsequence
|| (type == SqlVarType.hash && rowcount > 1) || isSelectableItem) {
hmap.put(varname, liste);
liste = null;
} else {
hmap.put(varname, entry);
entry = null;
}
}
}
}
/**
* Gets the result for sql query and returns it as a list of maps.
* Each map contains one row of the result with the column labels
* as keys (String, lower case!) and the data as values (String).
* @param sql to execute on this.dbHandler
* @return List of Map result of the sql query
* @throws SQLException
*/
protected List<TemplateHashModel> selectFromDb(String sql) throws SQLException {
List<TemplateHashModel> result = new ArrayList<>();
try (Statement st = con.createStatement();
ResultSet res = st.executeQuery(sql)) {
ResultSetMetaData meta = res.getMetaData();
int colCount = meta.getColumnCount();
List<String> colLabels = new ArrayList<>(colCount);
for (int i = 0; i < colCount; i++) {
colLabels.add(meta.getColumnLabel(i + 1));
}
while (res.next()) {
Map<String, String> row = new HashMap<>();
for (String colLabel : colLabels) {
row.put(colLabel, res.getString(colLabel));
}
result.add(new SimpleHash(row));
}
}
return result;
}
/**
* Konkret umgegesetzt in bin/FMParser
*
* @param hmap
* @param varname
* @param name_intern
* @param gewuenschterStand
* @return
* @throws SQLException
*/
protected StandaloneSicht addSqlVarSicht(HashMap hmap, String varname, String name_intern, String gewuenschterStand)
throws SQLException {
throw new IllegalStateException("sqlvar mit type Sicht wird hier nicht unterstützt");
}
protected SxResultSet readFromDb(String sql) throws SQLException {
return new SxResultSet();
}
private String removeSqlVars(String fmSql) {
int i = fmSql.indexOf("<sqlvars>");
int i2 = fmSql.indexOf("</sqlvars>");
return fmSql.substring(0, i) + fmSql.substring(i2 + 10, fmSql.length());
}
protected String cleanName(String templateName) {
// es dürfen keine nach Dir-Trennzeichen aussehenden Bezeichnungen
// vorkommen
if (templateName.indexOf("/") > -1)
templateName = templateName.replace('/', ' ');
if (templateName.indexOf("\\") > -1)
templateName = templateName.replace('\\', ' ');
return templateName;
}
public void setTemplates(SxResultSet rawFmTemplates) {
this.rawFmTemplates = rawFmTemplates;
if (rawFmTemplates == null) {
throw new RuntimeException("Keine FreeMarker Templates geliefert");
}
for (Iterator it = rawFmTemplates.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
String name = (String) row.get(0);
String content = SqlStringUtils.getValueAsString(row.get(1));
add(name, content);
cfg.addAutoInclude(name);
}
}
/**
* Repository INfo in eine Map packen
*
* @param Daten nach REPOSITORY_SELECT
* @param map
*/
public static void repositoryToMap(SxResultSet rs, HashMap map) {
IdObjectSet riColls = new IdObjectSet();
for (Iterator it = rs.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
Object id = row.get(0);
String name = (String) row.get(1);
RepositoryItem ri = new RepositoryItem();
ri.setId(id);
ri.setName(name);
ri.setContent(SqlStringUtils.getValueAsString(row.get(2)));
ri.setValidSince((Date) row.get(3));
ri.setValidTill((Date) row.get(4));
ri.setComment(SqlStringUtils.getValueAsString(row.get(5)));
// TODO sortnr
RepositoryItemCollection riColl = null;
if (riColls.containsItemWithId(id)) {
// Gibt schon eine Coll - einfach hinzufügen
riColl = (RepositoryItemCollection) riColls.getById(id);
} else {
// gibt noch keine Coll für id neu anlegen
riColl = new RepositoryItemCollection(id);
riColls.add(riColl);
}
riColl.add(ri);
}
for (Iterator it = riColls.iterator(); it.hasNext();) {
RepositoryItemCollection rcoll = (RepositoryItemCollection) it.next();
map.put(rcoll.getId(), rcoll);
}
}
class FMListe implements TemplateMethodModelEx, TemplateSequenceModel {
private static final long serialVersionUID = 1L;
private LinkedList liste = new LinkedList();
public void add(Object o) {
liste.add(o);
}
public Object exec(List l) throws TemplateModelException {
Object result = null;
if (l == null) {
throw new IllegalArgumentException("keine Argumente übergeben");
}
String key = "";
if (l.get(0).toString().equalsIgnoreCase("get")) {
if (l.size() < 3) {
throw new TemplateModelException("Syntax: get,id-column Schlüssel - mindestens 3 Elemente nötig");
}
String idcolumn = l.get(1).toString();
key = l.get(2).toString();
for (Iterator it = liste.iterator(); it.hasNext();) {
Object o = it.next();
if (o instanceof SxHash) {
SxHash h = (SxHash) o;
if (!h.containsKey(idcolumn))
throw new IllegalArgumentException("idcolumn " + idcolumn + " nicht gefunden");
if (h.get(idcolumn).toString().equals(key))
result = h;
}
}
} else if (l.get(0).toString().equalsIgnoreCase("getById")) {
if (l.size() < 2)
throw new TemplateModelException("Syntax: getbyId,Schlüssel - mindestens 2 Elemente nötig");
for (Iterator it = liste.iterator(); it.hasNext();) {
Object o = it.next();
if (o instanceof SelectableItem) {
SelectableItem s = (SelectableItem) o;
if (s.getId().toString().equals(key))
result = s;
}
}
} else
throw new IllegalArgumentException("nur getItem,Schlüssel implementiert");
if (result == null) {
throw new IllegalArgumentException("Element " + key + " nicht gefunden in Freemarker hash");
}
return result;
}
public TemplateModel get(int i) throws TemplateModelException {
Object o = liste.get(i);
TemplateModel result = null;
if (o instanceof SxHash) {
result = (SxHash) o;
} else {
result = new freemarker.template.DefaultObjectWrapper().wrap(o);
}
return result;
}
public int size() throws TemplateModelException {
return liste.size();
}
}
class SxHash implements TemplateHashModel {
private HashMap map = new HashMap();
public void put(String key, Object value) {
map.put(key, value);
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public TemplateModel get(String key) throws TemplateModelException {
if (!map.containsKey(key)) {
throw new IllegalArgumentException("Kein attribut " + key + " bekannt");
}
Object val = map.get(key);
if (val == null)
val = "";
TemplateModel result = null;
if (val instanceof String) {
result = new SimpleScalar(val.toString());
} else if (val instanceof java.sql.Date) {
result = new freemarker.template.SimpleDate((java.sql.Date) val);
} else if (val instanceof java.lang.Number) {
result = new freemarker.template.SimpleNumber((java.lang.Number) val);
} else if (val instanceof Clob) {
result = new SimpleScalar(SqlStringUtils.getValueAsString(val));
}
return result;
}
public boolean isEmpty() throws TemplateModelException {
return map.isEmpty();
}
}
}
// Created on 12.02.2005 at 13:46:05