/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.eas.client.model.store;
import com.eas.client.metadata.Field;
import com.eas.client.metadata.Fields;
import com.eas.client.metadata.ForeignKeySpec;
import com.eas.client.metadata.Parameter;
import com.eas.client.metadata.PrimaryKeySpec;
import com.eas.client.model.Entity;
import com.eas.client.model.Model;
import com.eas.client.model.Relation;
import com.eas.client.model.visitors.ModelVisitor;
import com.eas.client.queries.Query;
import com.eas.script.Scripts;
import com.eas.xml.dom.XmlDomUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.Time;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
*
* @author mg
* @param <E>
* @param <M>
*/
public abstract class XmlDom2Model<E extends Entity<M, ?, E>, M extends Model<E, ?>> implements ModelVisitor<E, M> {
public static final int DEFAULT_ENTITY_HEIGHT = 200;
public static final int DEFAULT_ENTITY_WIDTH = 150;
protected Document doc;
protected Element modelElement;
protected Element currentNode;
protected M currentModel;
protected Collection<Runnable> relationsResolvers = new ArrayList<>();
protected XmlDom2Model() {
super();
}
public Runnable readModel(final M aModel) {
Element elModel = doc != null ? doc.getDocumentElement() : modelElement;
if (elModel != null && aModel != null) {
currentModel = aModel;
try {
currentNode = elModel;
readEntities(aModel);
readRelations();
final Runnable[] resolvers = relationsResolvers.toArray(new Runnable[]{});
Runnable relationsResolver = () -> {
for (Runnable resolver : resolvers) {
resolver.run();
}
// Let's check relations in our model for integrity
aModel.checkRelationsIntegrity();
};
return relationsResolver;
} finally {
relationsResolvers.clear();
currentModel = null;
}
}
return null;
}
protected void readEntities(final M aModel) {
List<Element> dsnl = XmlDomUtils.elementsByTagName(currentNode, "e", Model2XmlDom.ENTITY_TAG_NAME);
List<Element> fnl = XmlDomUtils.elementsByTagName(currentNode, "fe", Model2XmlDom.FIELDS_ENTITY_TAG_NAME);
List<Element> nl = new ArrayList<>();
if (dsnl != null) {
nl.addAll(dsnl);
}
if (fnl != null) {
nl.addAll(fnl);
}
if (!nl.isEmpty()) {
Element lcurrentNode = currentNode;
try {
nl.stream().forEach((Element nl1) -> {
currentNode = nl1;
E entity = aModel.newGenericEntity();
entity.accept(this);
});
} finally {
currentNode = lcurrentNode;
}
}
}
public void readEntity(E entity) {
if (entity != null) {
entity.setEntityId(readLongAttribute(currentNode, "ei", Model2XmlDom.ENTITY_ID_ATTR_NAME, null));
if (XmlDomUtils.hasAttribute(currentNode, "qi", Model2XmlDom.QUERY_ID_ATTR_NAME)) {
String sQueryId = XmlDomUtils.getAttribute(currentNode, "qi", Model2XmlDom.QUERY_ID_ATTR_NAME);
if (!sQueryId.equals("null")) {
entity.setQueryName(sQueryId);
}
}
if (XmlDomUtils.hasAttribute(currentNode, "tbn", Model2XmlDom.TABLE_DB_ID_ATTR_NAME)) {
String aTableDbId = XmlDomUtils.getAttribute(currentNode, "tbn", Model2XmlDom.TABLE_DB_ID_ATTR_NAME);
if (!aTableDbId.equals("null")) {
entity.setTableDatasourceName(aTableDbId);
}
}
entity.setTableSchemaName(XmlDomUtils.getAttribute(currentNode, "tsn", Model2XmlDom.TABLE_SCHEMA_NAME_ATTR_NAME));
entity.setTableName(XmlDomUtils.getAttribute(currentNode, "tn", Model2XmlDom.TABLE_NAME_ATTR_NAME));
readEntityDesignAttributes(entity);
Model<E, ?> dm = entity.getModel();
if (dm != null) {
dm.addEntity(entity);
}
}
}
protected void readEntityDesignAttributes(E entity) {
entity.setX(readIntegerAttribute(currentNode, Model2XmlDom.ENTITY_LOCATION_X, Model2XmlDom.ENTITY_LOCATION_X, 0));
entity.setY(readIntegerAttribute(currentNode, Model2XmlDom.ENTITY_LOCATION_Y, Model2XmlDom.ENTITY_LOCATION_Y, 0));
entity.setWidth(readIntegerAttribute(currentNode, Model2XmlDom.ENTITY_SIZE_WIDTH, Model2XmlDom.ENTITY_SIZE_WIDTH, 0));
entity.setHeight(readIntegerAttribute(currentNode, Model2XmlDom.ENTITY_SIZE_HEIGHT, Model2XmlDom.ENTITY_SIZE_HEIGHT, 0));
entity.setIconified(readBooleanAttribute(currentNode, Model2XmlDom.ENTITY_ICONIFIED, Model2XmlDom.ENTITY_ICONIFIED, false));
}
@Override
public void visit(final Relation<E> relation) {
if (relation != null) {
final Long leftEntityId = readLongAttribute(currentNode, "lei", Model2XmlDom.LEFT_ENTITY_ID_ATTR_NAME, null);
final String leftFieldName = XmlDomUtils.getAttribute(currentNode, "lef", Model2XmlDom.LEFT_ENTITY_FIELD_ATTR_NAME);
final String leftParameterName = XmlDomUtils.getAttribute(currentNode, "lep", Model2XmlDom.LEFT_ENTITY_PARAMETER_ATTR_NAME);
final Long rightEntityId = readLongAttribute(currentNode, "rei", Model2XmlDom.RIGHT_ENTITY_ID_ATTR_NAME, null);
final String rightFieldName = XmlDomUtils.getAttribute(currentNode, "ref", Model2XmlDom.RIGHT_ENTITY_FIELD_ATTR_NAME);
final String rightParameterName = XmlDomUtils.getAttribute(currentNode, "rep", Model2XmlDom.RIGHT_ENTITY_PARAMETER_ATTR_NAME);
if (currentNode.hasAttribute(Model2XmlDom.POLYLINE_ATTR_NAME)) {
final String polyline = currentNode.getAttribute(Model2XmlDom.POLYLINE_ATTR_NAME);
if (polyline != null && !polyline.isEmpty()) {
readPolyline(polyline, relation);
}
}
M model = currentModel;
relationsResolvers.add((Runnable) () -> {
resolveRelation(model, leftEntityId, leftParameterName, relation, leftFieldName, rightEntityId, rightParameterName, rightFieldName);
});
}
}
protected void resolveRelation(final M aModel, final Long leftEntityId, final String leftParameterName, final Relation<E> relation, final String leftFieldName, final Long rightEntityId, final String rightParameterName, final String rightFieldName) {
try {
E lEntity = aModel.getEntityById(leftEntityId);
if (lEntity != null) {
Query query = lEntity.getQuery();
if (leftParameterName != null && !leftParameterName.isEmpty()) {
if (query != null) {
relation.setLeftField(query.getParameters().get(leftParameterName));
} else if (!aModel.isRelationsAgressiveCheck()) {
relation.setLeftField(new Parameter(leftParameterName));
}
} else if (leftFieldName != null && !leftFieldName.isEmpty()) {
Fields fields = lEntity.getFields();
if (fields != null && query.isMetadataAccessible()) {
relation.setLeftField(fields.get(leftFieldName));
} else if (!aModel.isRelationsAgressiveCheck()) {
relation.setLeftField(new Field(leftFieldName));
}
}
relation.setLeftEntity(lEntity);
lEntity.addOutRelation(relation);
}
E rEntity = aModel.getEntityById(rightEntityId);
if (rEntity != null) {
Query query = rEntity.getQuery();
if (rightParameterName != null && !rightParameterName.isEmpty()) {
if (query != null) {
relation.setRightField(query.getParameters().get(rightParameterName));
} else if (!aModel.isRelationsAgressiveCheck()) {
relation.setRightField(new Parameter(rightParameterName));
}
} else if (rightFieldName != null && !rightFieldName.isEmpty()) {
Fields fields = rEntity.getFields();
if (fields != null && query.isMetadataAccessible()) {
relation.setRightField(fields.get(rightFieldName));
} else if (!aModel.isRelationsAgressiveCheck()) {
relation.setRightField(new Field(rightFieldName));
}
}
relation.setRightEntity(rEntity);
rEntity.addInRelation(relation);
}
} catch (Exception ex) {
Logger.getLogger(XmlDom2Model.class.getName()).log(Level.WARNING, null, ex);
}
}
private void readPolyline(String aPolyline, Relation<E> aRelation) {
String[] points = aPolyline.split(" ");
if (points != null && points.length > 0) {
int[] xs = new int[points.length];
int[] ys = new int[points.length];
for (int i = 0; i < points.length; i++) {
String[] xy = points[i].split(";");
xs[i] = Integer.valueOf(xy[0]);
ys[i] = Integer.valueOf(xy[1]);
}
aRelation.setXYs(xs, ys);
}
}
private static final Set<String> legacyStringTypes = new HashSet<>(Arrays.asList(new String[]{"1", "12", "2005"}));
private static final Set<String> legacyNumberTypes = new HashSet<>(Arrays.asList(new String[]{"2", "3", "-5", "4", "5", "7", "8"}));
private static final Set<String> legacyDateTypes = new HashSet<>(Arrays.asList(new String[]{"91", "92", "93"}));
private static final Set<String> legacyBooleanTypes = new HashSet<>(Arrays.asList(new String[]{"-7", "16"}));
private static final Set<String> legacyNullTypes = new HashSet<>(Arrays.asList(new String[]{"2004", "1111"}));
@Override
public void visit(Field aField) {
try {
aField.setName(XmlDomUtils.getAttribute(currentNode, "n", Model2XmlDom.NAME_ATTR_NAME));
aField.setDescription(XmlDomUtils.getAttribute(currentNode, "d", Model2XmlDom.DESCRIPTION_ATTR_NAME));
String fieldType = XmlDomUtils.getAttribute(currentNode, "t", Model2XmlDom.TYPE_ATTR_NAME);
if (legacyStringTypes.contains(fieldType)) {
aField.setType(Scripts.STRING_TYPE_NAME);
} else if (legacyNumberTypes.contains(fieldType)) {
aField.setType(Scripts.NUMBER_TYPE_NAME);
} else if (legacyDateTypes.contains(fieldType)) {
aField.setType(Scripts.DATE_TYPE_NAME);
} else if (legacyBooleanTypes.contains(fieldType)) {
aField.setType(Scripts.BOOLEAN_TYPE_NAME);
} else if (legacyNullTypes.contains(fieldType)) {
aField.setType(null);
} else {
aField.setType(fieldType);// modern code :-)
}
aField.setNullable(readBooleanAttribute(currentNode, "nl", Model2XmlDom.NULLABLE_ATTR_NAME, true));
aField.setPk(readBooleanAttribute(currentNode, "p", Model2XmlDom.IS_PK_ATTR_NAME, false));
if (aField instanceof Parameter) {
((Parameter) aField).setMode(readIntegerAttribute(currentNode, "pm", Model2XmlDom.MODE_ATTR_NAME, ParameterMetaData.parameterModeUnknown));
((Parameter) aField).setSelectionForm(XmlDomUtils.getAttribute(currentNode, "sf", Model2XmlDom.SELECTION_FORM_TAG_NAME));
List<Element> dvEls = XmlDomUtils.elementsByTagName(currentNode, "dv", Model2XmlDom.DEFAULT_VALUE_TAG_NAME);
if (dvEls != null && dvEls.size() == 1) {
Element dvTag = dvEls.get(0);
String simpleClassName = XmlDomUtils.getAttribute(dvTag, "ch", Model2XmlDom.CLASS_HINT_TAG_NAME);
if (simpleClassName != null && !simpleClassName.isEmpty() && !simpleClassName.toLowerCase().equals("null")) {
String val = dvTag.getNodeValue();
if (val != null && !val.isEmpty() && !val.toLowerCase().equals("null")) {
Object dvO = readTypedValueFromString(simpleClassName.toLowerCase(), val);
((Parameter) aField).setDefaultValue(dvO);
}
}
}
}
Element lcurrentNode = currentNode;
try {
currentNode = XmlDomUtils.getElementByTagName(currentNode, "pr", Model2XmlDom.PRIMARY_KEY_TAG_NAME);
if (currentNode != null) {
PrimaryKeySpec pk = new PrimaryKeySpec();
visit(pk);
ForeignKeySpec fk = new ForeignKeySpec("", "", "", "", ForeignKeySpec.ForeignKeyRule.CASCADE, ForeignKeySpec.ForeignKeyRule.CASCADE, false, pk.getSchema(), pk.getTable(), pk.getField(), pk.getCName());
aField.setFk(fk);
}
} finally {
currentNode = lcurrentNode;
}
} catch (NumberFormatException ex) {
Logger.getLogger(XmlDom2Model.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void visit(PrimaryKeySpec pk) {
if (pk != null) {
pk.setCName(XmlDomUtils.getAttribute(currentNode, "n", Model2XmlDom.CONSTRAINT_NAME_ATTR_NAME));
pk.setSchema(XmlDomUtils.getAttribute(currentNode, "s", Model2XmlDom.CONSTRAINT_SCHEMA_ATTR_NAME));
pk.setTable(XmlDomUtils.getAttribute(currentNode, "tl", Model2XmlDom.CONSTRAINT_TABLE_ATTR_NAME));
pk.setField(XmlDomUtils.getAttribute(currentNode, "f", Model2XmlDom.CONSTRAINT_FIELD_ATTR_NAME));
}
}
protected Object readTypedValueFromString(String typeName, String aValue) {
Object resO = null;
if (typeName != null && !typeName.isEmpty() && aValue != null && !aValue.isEmpty()) {
aValue = aValue.trim();
if (!aValue.isEmpty() && !aValue.toLowerCase().equals("null")) {
try {
if (typeName.equalsIgnoreCase(Long.class.getSimpleName())) {
resO = Long.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Integer.class.getSimpleName())) {
resO = Integer.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Boolean.class.getSimpleName())) {
resO = Boolean.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Short.class.getSimpleName())) {
resO = Short.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Double.class.getSimpleName())) {
resO = Double.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Float.class.getSimpleName())) {
resO = Float.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(Byte.class.getSimpleName())) {
resO = Byte.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(BigDecimal.class.getSimpleName())) {
resO = BigDecimal.valueOf(Double.valueOf(aValue));
} else if (typeName.equalsIgnoreCase(BigInteger.class.getSimpleName())) {
resO = BigInteger.valueOf(Long.valueOf(aValue));
} else if (typeName.equalsIgnoreCase(java.sql.Date.class.getSimpleName())) {
resO = Date.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(java.sql.Time.class.getSimpleName())) {
resO = Time.valueOf(aValue);
} else if (typeName.equalsIgnoreCase(String.class.getSimpleName())) {
resO = aValue;
}
} catch (NumberFormatException ex) {
Logger.getLogger(XmlDom2Model.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return resO;
}
protected void readRelations() {
List<Element> nl = XmlDomUtils.elementsByTagName(currentNode, "r", Model2XmlDom.RELATION_TAG_NAME);
if (nl != null) {
Element lcurrentNode = currentNode;
try {
nl.stream().forEach((Element nl1) -> {
currentNode = nl1;
Relation<E> relation = new Relation<>();
relation.accept(this);
});
} finally {
currentNode = lcurrentNode;
}
}
}
protected static ForeignKeySpec.ForeignKeyRule readForeignKeyRuleAttribute(Element aElement, String aShortName, String aLongName, ForeignKeySpec.ForeignKeyRule defaultValue) {
try {
return ForeignKeySpec.ForeignKeyRule.valueOf(XmlDomUtils.getAttribute(aElement, aShortName, aLongName));
} catch (Exception ex) {
return defaultValue;
}
}
protected Long readLongAttribute(Element aElement, String aShortName, String aLongName, Long defaulValue) {
if (aElement.hasAttribute(aShortName)) {
return XmlDomUtils.readLongAttribute(aElement, aShortName, defaulValue);
} else {
return XmlDomUtils.readLongAttribute(aElement, aLongName, defaulValue);
}
}
protected static int readIntegerAttribute(Element aElement, String aShortName, String aLongName, int defaulValue) {
if (aElement.hasAttribute(aShortName)) {
return XmlDomUtils.readIntegerAttribute(aElement, aShortName, defaulValue);
} else {
return XmlDomUtils.readIntegerAttribute(aElement, aLongName, defaulValue);
}
}
protected static boolean readBooleanAttribute(Element aElement, String aShortName, String aLongName, boolean defaultValue) {
if (aElement.hasAttribute(aShortName)) {
return XmlDomUtils.readBooleanAttribute(aElement, aShortName, defaultValue);
} else {
return XmlDomUtils.readBooleanAttribute(aElement, aLongName, defaultValue);
}
}
/*
protected Object readTypedAttribute(String typeName, String name, Node node) {
Object resO = null;
if (typeName.equals(Long.class.getSimpleName().toLowerCase())) {
resO = readLongAttribute(name, null);
} else if (typeName.equals(Integer.class.getSimpleName().toLowerCase())) {
resO = readIntegerAttribute(name, null);
} else if (typeName.equals(Boolean.class.getSimpleName().toLowerCase())) {
resO = readBooleanAttribute(name, null);
} else if (typeName.equals(Short.class.getSimpleName().toLowerCase())) {
resO = readShortAttribute(name, null);
} else if (typeName.equals(Double.class.getSimpleName().toLowerCase())) {
resO = readDoubleAttribute(name, null);
} else if (typeName.equals(Float.class.getSimpleName().toLowerCase())) {
resO = readFloatAttribute(name, null);
} else if (typeName.equals(Byte.class.getSimpleName().toLowerCase())) {
resO = readByteAttribute(name, null);
} else if (typeName.equals(BigDecimal.class.getSimpleName().toLowerCase())) {
resO = readBigDecimalAttribute(name, null);
} else if (typeName.equals(BigInteger.class.getSimpleName().toLowerCase())) {
resO = readBigIntegerAttribute(name, null);
} else if (typeName.equals(java.sql.Date.class.getSimpleName().toLowerCase())) {
resO = readDateAttribute(name, null);
} else if (typeName.equals(java.sql.Time.class.getSimpleName().toLowerCase())) {
resO = readTimeAttribute(name, null);
} else if (typeName.equals(String.class.getSimpleName().toLowerCase())) {
resO = node.getNodeValue();
}
return resO;
}
protected Short readShortAttribute(String attributeName, Short defaulValue) {
return XmlDomUtils.readShortAttribute(currentNode, attributeName, defaulValue);
}
protected Double readDoubleAttribute(String attributeName, Double defaultValue) {
return XmlDomUtils.readDoubleAttribute(currentNode, attributeName, defaultValue);
}
protected Float readFloatAttribute(String attributeName, Float defaultValue) {
return XmlDomUtils.readFloatAttribute(currentNode, attributeName, defaultValue);
}
protected Byte readByteAttribute(String attributeName, Byte defaultValue) {
return XmlDomUtils.readByteAttribute(currentNode, attributeName, defaultValue);
}
protected BigDecimal readBigDecimalAttribute(String attributeName, BigDecimal defaultValue) {
return XmlDomUtils.readBigDecimalAttribute(currentNode, attributeName, defaultValue);
}
protected BigInteger readBigIntegerAttribute(String attributeName, BigInteger defaultValue) {
return XmlDomUtils.readBigIntegerAttribute(currentNode, attributeName, defaultValue);
}
protected Date readDateAttribute(String attributeName, Date defaultValue) {
return XmlDomUtils.readDateAttribute(currentNode, attributeName, defaultValue);
}
protected Time readTimeAttribute(String attributeName, Time defaultValue) {
return XmlDomUtils.readTimeAttribute(currentNode, attributeName, defaultValue);
}
*/
}