Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.odysz.module.xtable.XMLTable Maven / Gradle / Ivy
package io.odysz.module.xtable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class XMLTable {
public interface IMapValue {
String mapKey();
}
@FunctionalInterface
public interface IParser {
/**
* Parameter t is iterated with "while(next()".
* The t.rowIdx is the current row's index.
* @param t
* @return parsed object
* @throws SAXException
*/
T parse(XMLTable t) throws Exception;
}
private static final String TAG = "XMLTable";
private ILogger logger;
protected String _tableID;
protected HashMap columns;
protected HashMap pkCols;
protected ArrayList rows;
protected String[] currentRec;
/** Construct an empty table according to cols and pk.
* Table construction not finished without calling endTablePush().
* @param tableID
* @param cols
* @param pk
* @throws Exception
*/
public XMLTable(String tableID, String cols, String pk, ILogger log) throws SAXException {
isBuilding = true;
// logger = XMLDataFactory.getLogger();
logger = log;
// build columns
if (cols == null || cols.trim().equals("")) {
logger.e(TAG, "Can not construct table " + tableID + " with empty col specification");
return;
}
if (tableID == null || "".equals(tableID.trim()))
tableID = "XMLTable.Generated Default Table ID";
else _tableID = tableID;
String[] colNames = cols.split(",");
columns = new LinkedHashMap();
for (int i = 0; i < colNames.length; i++) {
columns.put(colNames[i].trim(), i);
}
if (pk != null && !"".equals(pk.trim())) {
pkCols = new LinkedHashMap();
String[] pkNames = pk.split(",");
for (int i = 0; i < pkNames.length; i++) {
pkCols.put(pkNames[i].trim(), columns.get(pkNames[i].trim()));
}
}
rows = new ArrayList();
}
/**
* Constructing an empty table, table structure value copied from parameters.
* Table construction not finished without calling endTablePush().
* This is useful for reduced rows and cols table copying.
* @param tableID
* @param fromColumns
* @param fromPkCols
*/
public XMLTable(String tableID, HashMap fromColumns,
HashMap fromPkCols) {
isBuilding = true;
_tableID = tableID;
columns = new HashMap();
for (String cname : fromColumns.keySet()) {
columns.put(new String(cname), fromColumns.get(cname));
}
pkCols = new HashMap();
for (String cname : fromPkCols.keySet()) {
pkCols.put(new String(cname), fromPkCols.get(cname));
}
rows = new ArrayList();
}
/**
* value copy and construct a new table, according to parameters.
* @param newTableID
* @param fromColumns
* @param fromPkCols
* @param fromList
*/
public XMLTable(String newTableID, HashMap fromColumns,
HashMap fromPkCols, ArrayList fromList) {
_tableID = newTableID;
columns = new HashMap();
for (String cname : fromColumns.keySet()) {
columns.put(new String(cname), fromColumns.get(cname));
}
pkCols = new HashMap();
for (String cname : fromPkCols.keySet()) {
pkCols.put(new String(cname), fromPkCols.get(cname));
}
rows = new ArrayList();
for (String[] row : fromList) {
String[] newRow = new String[columns.size()];
for (String k : columns.keySet()) {
if (row[columns.get(k)] != null)
newRow[columns.get(k)] = new String(row[columns.get(k)]);
}
rows.add(newRow);
}
isBuilding = false;
}
/**This can be used to clone from FLResultset.
* Becareful, the cols and rows indexes in result set start at 1, but in XMLTable that start at 0!
* All columns and rows idxes are -1.
* @param fromColumns
* @param fromPkCols
* @param fromList
*/
public XMLTable(HashMap fromColumns,
HashMap fromPkCols, ArrayList> fromList) {
_tableID = "table1";
columns = new HashMap();
for (String cname : fromColumns.keySet()) {
columns.put(new String(cname), fromColumns.get(cname) - 1);
}
if (fromPkCols != null && fromPkCols.size() > 0) {
pkCols = new HashMap();
for (String cname : fromPkCols.keySet())
pkCols.put(new String(cname), columns.get(cname));
}
rows = new ArrayList();
for (ArrayList row : fromList) {
String[] newRow = new String[columns.size()];
for (int i = 0; i < columns.size(); i++) {
if (row.get(i) != null)
newRow[i] = row.get(i).toString();
}
rows.add(newRow);
}
isBuilding = false;
}
protected void appendFieldValue(String colName, String fieldValue) throws SAXParseException {
validateTableStruct();
if (colName == null || "".equals(colName.trim()) || !columns.containsKey(colName.trim())) {
logger.w(TAG, "Can not resolve colName to append value. Column ignored: " + colName + ", tableID: " + _tableID);
return;
}
appendFieldValue(columns.get(colName), fieldValue);
}
protected void appendFieldValue(int colIdx, String fieldValue) throws SAXParseException {
if (colIdx < 0 || colIdx > columns.size()) throw new SAXParseException("Column index out of bundary", null);
currentRec[colIdx] = fieldValue;
}
protected void startRecordPush() throws SAXParseException {
validateTableStruct();
currentRec = new String[columns.size()];
}
/**
* Push the temp record into table rows.
* For data validation, checkPK shall be true.
* But it's a serious performance defect. Set checkPK to false when deploying a release version.
* @param checkPK
* @throws SAXParseException
*/
protected void endRecordPush(boolean checkPK) throws SAXParseException {
if (currentRec == null) {
logger.e(TAG, String.format("Find a null record, check record format in table %s is correct.", _tableID));
return;
}
if (checkPK && pkCols != null && pkCols.size() > 0) {
String[] pkVals = new String[pkCols.size()];
logger.i(TAG, "Parsing record...");
// check null pk
int c = 0;
for (String col : pkCols.keySet()) {
if (pkCols.get(col) == null || currentRec[pkCols.get(col)] == null) {
logger.e(TAG, "PK value can not be null. tableID: " + _tableID + ", col: " + col);
throw new SAXParseException("PK value can not be null. tableID: " + _tableID + ", col: " + col, null);
}
else {
pkVals[c] = currentRec[pkCols.get(col)];
logger.i(TAG, "\tPK = " + pkVals[c]);
c++;
}
}
// check duplicated pk
if (currentRec != null && getRecordByPK(pkVals) != null) {
logger.e(TAG, "Record ignored for duplicated pk from table " + _tableID + ": ");
for (int i = 0; i < pkVals.length; i++)
logger.e(" ", pkVals[i]);
}
}
rows.add(currentRec);
currentRec = null;
}
/**
* Find record according to pk values.
* pkVals is exactly the same order specified in xmlfile/table pk attribute.
* IMPORTANT: There are no index or any other performance facilities, be careful!
* @param pkVals
* @return target record
* @throws SAXParseException
*/
public String[] getRecordByPK(String[] pkVals) throws SAXParseException {
if (rows == null || rows.size() <= 0) return null;
if (pkCols == null || pkCols.size() <= 0)
throw new SAXParseException("Table " + _tableID + " can not support getRecordByPK() without PK specification.", null);
for (int i = 0; i < rows.size(); i++) {
boolean found = false;
int j = 0;
for (String k : pkCols.keySet()) {
if (rows.get(i)[pkCols.get(k)].equals(pkVals[j])) {
j++;
found = true;
}
else {
found = false;
break;
}
}
if (found) {
return rows.get(i);
}
}
return null;
}
private boolean isBuilding = false;
public void startTablePush() {
isBuilding = true;
}
public XMLTable endTablePush() {
isBuilding = false;
try {
beforeFirst();
} catch (SAXException e) {
logger.e(TAG, "Why reached here?");
e.printStackTrace();
}
return this;
}
private void validateTableStruct() throws SAXParseException {
if (columns == null) throw new SAXParseException("Table Structure invalidate.", null);
}
////////////////////////////////////////////////////////////////////
int rowIdx = -1;
public XMLTable beforeFirst() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
rowIdx = -1;
return this;
}
/** go last record - getXXX(c) got last row's field.
* @throws SAXException
*/
public void last() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
goAt(getRowCount() - 1);
}
public boolean next() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
if (rows == null || rows.size() <= 0) return false;
if (rowIdx < rows.size() - 1) {
rowIdx++;
return true;
}
else return false;
}
public boolean goAt(int position) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
if (rows == null || rows.size() <= 0) return false;
if (position < 0 || position >= rows.size())
throw new SAXException("Target position out of boundary.", null);
rowIdx = position;
return true;
}
public void end() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
rowIdx = rows.size() - 1;
}
public boolean previous() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
if (rows == null || rows.size() <= 0) return false;
if (rowIdx > 0 && rowIdx < rows.size()) {
rowIdx--;
return true;
}
else return false;
}
public String[] getRow() throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
return rows.get(rowIdx);
}
/**
* @param col start from 0.
* @return target field value
* @throws SAXException
*/
public String getString(int col) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
validateTableStruct();
if (columns.size() < col || col < 0) throw new SAXException("Column index outof bundary.", null);
return rows.get(rowIdx)[col];
}
public String getString(String colName) throws SAXException {
validateTableStruct();
int index = columns.get(colName);
String ccc = getString(index);
return ccc;
}
public String getStringAt(int row, String colName) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
validateTableStruct();
int col = columns.get(colName);
if (columns.size() < col || col < 0) throw new SAXException("Column index outof bundary.", null);
return rows.get(row)[col];
}
public String getTableID() {
return _tableID;
}
public int getRowCount() {
if (rows == null) return 0;
return rows.size();
}
public int getRowIdx() {return rowIdx;}
/**
* @param kv - [key = colname, val = fieldVal]
* @return record list
* @throws SAXException
*/
public ArrayList findRecords(HashMap kv) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
ArrayList retList = new ArrayList();
if (rows == null) return retList;
for (int i = 0; i < rows.size(); i++) {
boolean matched = false;
for (String col : kv.keySet()) {
int c = columns.get(col);
if (rows.get(i)[c] == null || !rows.get(i)[c].equals(kv.get(col))) {
matched = false;
break;
}
else matched = true;
}
if (matched) retList.add(rows.get(i));
}
return retList;
}
/**
* @param pkVals - [key = colname, val = fieldVal]
* @return target table
* @throws SAXException
*/
public XMLTable findRecordsTable(HashMap pkVals) throws SAXException {
XMLTable retTable = new XMLTable(_tableID, columns, pkCols);
retTable.copyRows(findRecords(pkVals));
retTable.endTablePush();
return retTable;
}
public String[] getRowAt(int position) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
return rows.get(position);
}
private void copyRows(ArrayList fromRows) {
for (String[] row : fromRows) {
String[] newRow = new String[columns.size()];
for (int i = 0; i < columns.size(); i++) {
if (row[i] != null) {
String v = new String(row[i]);
newRow[i] = v;
}
}
rows.add(newRow);
}
}
public HashMap getColumns() {
return columns;
}
public HashMap getPKs() {
return pkCols;
}
/**Clone a column index for id reference.
* Sometimes the E2Engine will refere to a fixed name field to retrieve data, e.g. "itemId".
* @param newName
* @param oldName
* @throws SAXException
*/
public void cloneCol(String newName, String oldName) throws SAXException {
if (columns == null)
throw new SAXException("There is no columns to rename.");
if (columns.containsKey(newName))
throw new SAXException("New columns name already eaxists.");
if (!columns.containsKey(oldName))
throw new SAXException(String.format("There is no column named %s to rename.", oldName));
int colIdx = columns.get(oldName);
columns.put(newName, colIdx);
if (pkCols != null && pkCols.containsKey(oldName))
pkCols.put(newName, colIdx);
}
public ArrayList getRows() {
return rows;
}
public String[] getRows(String colName) throws SAXException {
String[] cells = new String[rows.size()];
beforeFirst();
int i = 0;
while (next()) {
cells[i] = getString(colName);
i++;
}
return cells;
}
public int getInt(String colName, int defaultVal) {
try { return getInt(colName);}
catch (Exception ex) { return defaultVal;}
}
public int getInt(String colName) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
return getInt(index);
}
public int getInt(int col, int defaultVal) {
try { return getInt(col);}
catch (Exception ex) { return defaultVal;}
}
private int getInt(int col) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
validateTableStruct();
if (columns.size() < col || col < 0) throw new SAXException("Column index outof bundary.", null);
String v = rows.get(rowIdx)[col];
return Integer.valueOf(v);
}
public int getIntAt(int row, String colName, int defaultVal) {
try { return getIntAt(row, columns.get(colName), defaultVal); }
catch (Exception ex) {return defaultVal;}
}
public int getIntAt(int row, int col, int defaultVal) {
try {
String v = rows.get(row)[col];
return Integer.valueOf(v);
} catch (Exception ex) {return defaultVal;}
}
public boolean getBool(String colName, boolean defaultVal) {
try { return getBool(colName);}
catch (Exception ex) { return defaultVal;}
}
private boolean getBool(String colName) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
return getBool(index);
}
private boolean getBool(int col) throws SAXException {
if (isBuilding) throw new SAXException("Can not move cursor while building table.", null);
validateTableStruct();
if (columns.size() < col || col < 0) throw new SAXException("Column index outof bundary.", null);
String v = rows.get(rowIdx)[col];
if (v == null) return false;
v = v.trim().toLowerCase();
if (v.length() == 0 || v.length() > 4) return false;
if ("1".equals(v) || "true".equals(v) || "y".equals(v) || "t".equals(v) || "yes".equals(v) || "ok".equals(v))
return true;
return false;
}
public String[] getStrings(String colName) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
String fstr = getString(index);
if (fstr == null) return null;
else
return fstr.split(",", -1);
}
public float getFloat(String colName, float defaultVal) {
try {
String v = getString(colName).trim();
return Float.valueOf(v);
}catch (Exception e) {
// e.printStackTrace();
return defaultVal;
}
}
public float[] getFloats(String colName, float defaultVal) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
String fstr = getString(index);
if (fstr == null) return null;
else {
String[] fs = fstr.split(",", -1);
float[] vfs = new float[fs.length];
for (int i = 0; i < fs.length; i++) {
try { vfs[i] = Float.valueOf(fs[i].trim());
} catch (Exception e) { vfs[i] = defaultVal; }
}
return vfs;
}
}
public int[] getInts(String colName, int defaultVal) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
String istr = getString(index);
if (istr == null) return null;
else {
String[] ss = istr.split(",", -1);
int[] vis = new int[ss.length];
for (int i = 0; i < ss.length; i++) {
try { vis[i] = Integer.valueOf(ss[i].trim());
} catch (Exception e) { vis[i] = defaultVal; }
}
return vis;
}
}
private HashMap _tableAttrs;
public void setXmlAttrs(Attributes attributes) {
_tableAttrs = new HashMap();
for (int i = 0; i < attributes.getLength(); i++) {
String k = attributes.getQName(i);
String v = attributes.getValue(i);
_tableAttrs.put(k, v);
}
}
public String getAttribute(String attr, String defaultVal) {
if ("package".equals(attr)) return "enlearn";
if (_tableAttrs == null) return defaultVal;
else {
String v = _tableAttrs.get(attr);
if ("package".equals(attr) && "0".equals(v)) return "defaultVal";
else return v;
}
}
public void setField(String colName, String val) throws SAXException {
validateTableStruct();
int index=columns.get(colName);
rows.get(rowIdx)[index] = val;
}
/**If pk is duplicating, merging record is ignored. */
public static final int duplicateIgnor = 101;
/**If pk is duplicating, this obj's record is replaced with merging. */
public static final int duplicateReplace = 102;
/**Merge withTable to this object.
* Table name and colomns order can be different, but both table must have exactly the same columns and pk fields.
* Note:
* This method is not suitable for large record's number. There is a performance problem,
* especially when both table has many duplicated records. xTable is not designed for DB functioning,
* it's designed for retrieve data such as App configurations.
* @param withTable
* @param duplicateMode one of {@link XMLTable#duplicateIgnor} and {@link XMLTable#duplicateReplace}
* @throws SAXException
*/
public void mergeWith(XMLTable withTable, int duplicateMode) throws SAXException {
if (withTable == null)
return;
if (isBuilding || withTable.isBuilding)
throw new SAXException(String.format("Can't merge (table=%s) if one or both tables is being built.",
getTableID()));
if (columns == null || withTable.columns == null || columns.size() != withTable.columns.size())
throw new SAXException(String.format("Can't merge (table=%s) if both tables have different column size",
getTableID()));
for (String c : columns.keySet()) {
if (!withTable.columns.containsKey(c))
throw new SAXException(String.format("The other table(withTable=%s) doesn't have a column %s.",
getTableID(), c));
}
HashMap kv = new HashMap();
withTable.beforeFirst();
while (withTable.next()) {
if (pkCols != null) {
kv.clear();
for (String pk : pkCols.keySet()) {
kv.put(pk, withTable.getString(pk));
}
ArrayList dupList = findRecords(kv);
if (dupList != null && dupList.size() > 0) {
switch (duplicateMode) {
case duplicateIgnor:
// 1 ignore duplicated row - no cloning
break;
case duplicateReplace:
// 2 replace row with cloned
remove(kv);
cloneRow(withTable);
break;
default:
// x no correct duplicating way
throw new SAXException(String.format("The record from merging table (row number = %s) is duplicating with this table, but no duplicate operation specificed correctly. Check parameter duplicateMode's value.", withTable.getRowIdx()));
}
}
else
// 3 clone a non duplicated row
cloneRow(withTable);
}
// clone row without pk
else cloneRow(withTable);
}
}
private void cloneRow(XMLTable withTable) throws SAXException {
// HashMap newRow = new HashMap();
// for (String c : columns.keySet()) {
// newRow.put(c, withTable.getString(c));
// }
// appendRow(newRow);
String[] newRow = new String[columns.size()];
for (String c : columns.keySet()) {
newRow[columns.get(c)] = withTable.getString(c);
}
rows.add(newRow);
}
public void appendRow(HashMap row) throws SAXException {
// if (!isBuilding || columns == null || columns.size() < row.size())
// throw new SAXException("Can't append row if table been built or columns' size less than row's fields.");
//
// if (row == null || row.keySet() == null || row.keySet().size() == 0)
// return;
//
// for (String rk : row.keySet()) {
// if (!columns.containsKey(rk))
// throw new SAXException(String.format("Row with column %s can't append to table %s.", rk, _tableID));
// }
//
// String[] newRow = new String[columns.size()];
// for (String c : columns.keySet()) {
// newRow[columns.get(c)] = row.get(c);
// }
// rows.add(newRow);
if (rows == null) {
rows = new ArrayList();
rowIdx = -1;
}
insertRowAt(rows.size(), row);
}
public void insertRowAt(int rowIdx, HashMap row) throws SAXException {
if (!isBuilding || columns == null || columns.size() < row.size())
throw new SAXException("Can't insert row if table been built or columns' size less than row's fields.");
if (row == null || row.keySet() == null || row.keySet().size() == 0)
return;
for (String rk : row.keySet()) {
if (!columns.containsKey(rk))
throw new SAXException(String.format("Row with column %s can't append to table %s.", rk, _tableID));
}
String[] newRow = new String[columns.size()];
for (String c : columns.keySet()) {
newRow[columns.get(c)] = row.get(c);
}
rows.add(rowIdx, newRow);
}
public void remove(HashMap kv) throws SAXException {
if (isBuilding) throw new SAXException("Can not remove while building table.", null);
if (rows == null) return;
for (int i = 0; i < rows.size(); i++) {
boolean matched = false;
for (String col : kv.keySet()) {
int c = columns.get(col);
if (rows.get(i)[c] == null || !rows.get(i)[c].equals(kv.get(col))) {
matched = false;
break;
}
else matched = true;
}
if (matched) {
rows.remove(i);
return;
}
}
}
public HashMap map(IParser parser)
throws Exception {
HashMap m = new HashMap(getRowCount());
beforeFirst();
while (next()) {
// String tabl = xcfg.getString("tabl");
// String pk = xcfg.getString("pk");
// String smtc = xcfg.getString("smtc");
// String args = xcfg.getString("args");
// try {
// addSemantics(conn, tabl, pk, smtc, args, debug);
// } catch (SemanticException e) {
// Utils.warn(e.getMessage());
// }
T v = parser.parse(this);
if (v != null)
m.put(v.mapKey(), v);
}
return m;
}
}