blob: 234013069965ce3e81405aef317a90ac0adb84fe [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.ofbiz.entity.model;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.StringUtil;
import org.ofbiz.base.util.UtilFormatOut;
import org.ofbiz.base.util.UtilTimer;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.base.util.UtilXml;
import org.ofbiz.entity.condition.EntityComparisonOperator;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityConditionValue;
import org.ofbiz.entity.condition.EntityFieldValue;
import org.ofbiz.entity.condition.EntityFunction;
import org.ofbiz.entity.condition.EntityJoinOperator;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.entity.jdbc.SqlJdbcUtil;
import org.ofbiz.entity.util.EntityUtil;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* This class extends ModelEntity and provides additional information appropriate to view entities
*/
@SuppressWarnings("serial")
public class ModelViewEntity extends ModelEntity {
public static final String module = ModelViewEntity.class.getName();
private static final Map<String, String> functionPrefixMap = new HashMap<String, String>();
private static final Set<String> numericFunctionsSet = new HashSet<String>(); // names of functions that return a numeric type
static {
functionPrefixMap.put("min", "MIN(");
functionPrefixMap.put("max", "MAX(");
functionPrefixMap.put("sum", "SUM(");
functionPrefixMap.put("avg", "AVG(");
functionPrefixMap.put("count", "COUNT(");
functionPrefixMap.put("count-distinct", "COUNT(DISTINCT ");
functionPrefixMap.put("upper", "UPPER(");
functionPrefixMap.put("lower", "LOWER(");
functionPrefixMap.put("extract-year", "EXTRACT(YEAR FROM ");
functionPrefixMap.put("extract-month", "EXTRACT(MONTH FROM ");
functionPrefixMap.put("extract-day", "EXTRACT(DAY FROM ");
numericFunctionsSet.add("count");
numericFunctionsSet.add("count-distinct");
numericFunctionsSet.add("extract-year");
numericFunctionsSet.add("extract-month");
numericFunctionsSet.add("extract-day");
}
/** Contains member-entity alias name definitions: key is alias, value is ModelMemberEntity */
protected Map<String, ModelMemberEntity> memberModelMemberEntities = new HashMap<String, ModelMemberEntity>();
/** A list of all ModelMemberEntity entries; this is mainly used to preserve the original order of member entities from the XML file */
protected List<ModelMemberEntity> allModelMemberEntities = new LinkedList<ModelMemberEntity>();
/** Contains member-entity ModelEntities: key is alias, value is ModelEntity; populated with fields */
protected Map<String, String> memberModelEntities = new HashMap<String, String>();
/** List of alias-alls which act as a shortcut for easily pulling over member entity fields */
protected List<ModelAliasAll> aliasAlls = new LinkedList<ModelAliasAll>();
/** List of aliases with information in addition to what is in the standard field list */
protected List<ModelAlias> aliases = new LinkedList<ModelAlias>();
/** List of view links to define how entities are connected (or "joined") */
protected List<ModelViewLink> viewLinks = new LinkedList<ModelViewLink>();
/** A List of the Field objects for the View Entity, one for each GROUP BY field */
protected List<ModelField> groupBys = new LinkedList<ModelField>();
/** List of field names to group by */
protected List<String> groupByFields = new LinkedList<String>();
protected Map<String, ModelConversion[]> conversions = new HashMap<String, ModelConversion[]>();
protected ViewEntityCondition viewEntityCondition = null;
public ModelViewEntity(ModelReader reader, Element entityElement, UtilTimer utilTimer, ModelInfo def) {
super(reader, entityElement, def);
if (utilTimer != null) utilTimer.timerString(" createModelViewEntity: before general/basic info");
this.populateBasicInfo(entityElement);
if (utilTimer != null) utilTimer.timerString(" createModelViewEntity: before \"member-entity\"s");
for (Element memberEntityElement: UtilXml.childElementList(entityElement, "member-entity")) {
String alias = UtilXml.checkEmpty(memberEntityElement.getAttribute("entity-alias")).intern();
String name = UtilXml.checkEmpty(memberEntityElement.getAttribute("entity-name")).intern();
if (name.length() <= 0 || alias.length() <= 0) {
Debug.logError("[new ModelViewEntity]: entity-alias or entity-name missing on member-entity element of the view-entity " + this.entityName, module);
} else {
ModelMemberEntity modelMemberEntity = new ModelMemberEntity(alias, name);
this.addMemberModelMemberEntity(modelMemberEntity);
}
}
// when reading aliases and alias-alls, just read them into the alias list, there will be a pass
// after loading all entities to go back and fill in all of the ModelField entries
for (Element aliasElement: UtilXml.childElementList(entityElement, "alias-all")) {
ModelViewEntity.ModelAliasAll aliasAll = new ModelAliasAll(aliasElement);
this.aliasAlls.add(aliasAll);
}
if (utilTimer != null) utilTimer.timerString(" createModelViewEntity: before aliases");
for (Element aliasElement: UtilXml.childElementList(entityElement, "alias")) {
ModelViewEntity.ModelAlias alias = new ModelAlias(aliasElement);
this.aliases.add(alias);
}
for (Element viewLinkElement: UtilXml.childElementList(entityElement, "view-link")) {
ModelViewLink viewLink = new ModelViewLink(this, viewLinkElement);
this.addViewLink(viewLink);
}
if (utilTimer != null) utilTimer.timerString(" createModelEntity: before relations");
this.populateRelated(reader, entityElement);
Element entityConditionElement = UtilXml.firstChildElement(entityElement, "entity-condition");
if (entityConditionElement != null) {
this.viewEntityCondition = new ViewEntityCondition(this, null, entityConditionElement);
}
// before finishing, make sure the table name is null, this should help bring up errors early...
this.tableName = null;
}
public ModelViewEntity(DynamicViewEntity dynamicViewEntity, ModelReader modelReader) {
super(modelReader, new ModelInfo(
dynamicViewEntity.getTitle(),
ModelInfo.DEFAULT.getDescription(),
ModelInfo.DEFAULT.getCopyright(),
ModelInfo.DEFAULT.getAuthor(),
ModelInfo.DEFAULT.getVersion(),
dynamicViewEntity.getDefaultResourceName()));
this.entityName = dynamicViewEntity.getEntityName();
this.packageName = dynamicViewEntity.getPackageName();
// member-entities
Iterator<Map.Entry<String, ModelMemberEntity>> modelMemberEntitiesEntryIter = dynamicViewEntity.getModelMemberEntitiesEntryIter();
while (modelMemberEntitiesEntryIter.hasNext()) {
Map.Entry<String, ModelMemberEntity> entry = modelMemberEntitiesEntryIter.next();
this.addMemberModelMemberEntity(entry.getValue());
}
// alias-alls
dynamicViewEntity.addAllAliasAllsToList(this.aliasAlls);
// aliases
dynamicViewEntity.addAllAliasesToList(this.aliases);
// view-links
dynamicViewEntity.addAllViewLinksToList(this.viewLinks);
// relations
dynamicViewEntity.addAllRelationsToList(this.relations);
dynamicViewEntity.addAllGroupByFieldsToList(this.groupByFields);
// finalize stuff
// note that this doesn't result in a call to populateReverseLinks because a DynamicViewEntity should never be cached anyway, and will blow up when attempting to make the reverse links to the DynamicViewEntity
this.populateFieldsBasic(modelReader);
}
public Map<String, ModelMemberEntity> getMemberModelMemberEntities() {
return this.memberModelMemberEntities;
}
public List<ModelMemberEntity> getAllModelMemberEntities() {
return this.allModelMemberEntities;
}
public ModelMemberEntity getMemberModelMemberEntity(String alias) {
return this.memberModelMemberEntities.get(alias);
}
public ModelEntity getMemberModelEntity(String alias) {
String entityName = this.memberModelEntities.get(alias);
return entityName != null ? this.getModelReader().getModelEntityNoCheck(entityName) : null;
}
public void addMemberModelMemberEntity(ModelMemberEntity modelMemberEntity) {
this.memberModelMemberEntities.put(modelMemberEntity.getEntityAlias(), modelMemberEntity);
this.allModelMemberEntities.add(modelMemberEntity);
}
public void removeMemberModelMemberEntity(String alias) {
ModelMemberEntity modelMemberEntity = this.memberModelMemberEntities.remove(alias);
if (modelMemberEntity == null) return;
this.allModelMemberEntities.remove(modelMemberEntity);
}
/** The col-name of the Field, the alias of the field if this is on a view-entity */
@Override
public String getColNameOrAlias(String fieldName) {
ModelField modelField = this.getField(fieldName);
String fieldString = modelField.getColName();
ModelViewEntity.ModelAlias alias = getAlias(fieldName);
if (alias != null) {
fieldString = alias.getColAlias();
}
return fieldString;
}
/** List of aliases with information in addition to what is in the standard field list */
public ModelAlias getAlias(int index) {
return this.aliases.get(index);
}
public ModelAlias getAlias(String name) {
Iterator<ModelAlias> aliasIter = getAliasesIterator();
while (aliasIter.hasNext()) {
ModelAlias alias = aliasIter.next();
if (alias.name.equals(name)) {
return alias;
}
}
return null;
}
public int getAliasesSize() {
return this.aliases.size();
}
public Iterator<ModelAlias> getAliasesIterator() {
return this.aliases.iterator();
}
public List<ModelAlias> getAliasesCopy() {
List<ModelAlias> newList = new ArrayList<ModelAlias>(this.aliases);
return newList;
}
public int getGroupBysSize() {
return this.groupBys.size();
}
public List<ModelField> getGroupBysCopy() {
return getGroupBysCopy(null);
}
public List<ModelField> getGroupBysCopy(List<ModelField> selectFields) {
List<ModelField> newList = new ArrayList<ModelField>(this.groupBys.size());
if (UtilValidate.isEmpty(selectFields)) {
newList.addAll(this.groupBys);
} else {
for (ModelField groupByField: this.groupBys) {
if (selectFields.contains(groupByField)) {
newList.add(groupByField);
}
}
}
return newList;
}
/** List of view links to define how entities are connected (or "joined") */
public ModelViewLink getViewLink(int index) {
return this.viewLinks.get(index);
}
public int getViewLinksSize() {
return this.viewLinks.size();
}
public Iterator<ModelViewLink> getViewLinksIterator() {
return this.viewLinks.iterator();
}
public List<ModelViewLink> getViewLinksCopy() {
List<ModelViewLink> newList = new ArrayList<ModelViewLink>(this.viewLinks);
return newList;
}
public void addViewLink(ModelViewLink viewLink) {
this.viewLinks.add(viewLink);
}
public void populateViewEntityConditionInformation(ModelFieldTypeReader modelFieldTypeReader, List<EntityCondition> whereConditions, List<EntityCondition> havingConditions, List<String> orderByList, List<String> entityAliasStack) {
if (entityAliasStack == null) {
entityAliasStack = new LinkedList<String>();
}
if (this.viewEntityCondition != null) {
EntityCondition whereCondition = this.viewEntityCondition.getWhereCondition(modelFieldTypeReader, entityAliasStack);
if (whereCondition != null) {
whereConditions.add(whereCondition);
}
EntityCondition havingCondition = this.viewEntityCondition.getHavingCondition(modelFieldTypeReader, entityAliasStack);
if (havingCondition != null) {
havingConditions.add(havingCondition);
}
// add the current one first so it overrides the lower level ones
List<String> currentOrderByList = this.viewEntityCondition.getOrderByList();
if (currentOrderByList != null) {
orderByList.addAll(currentOrderByList);
}
}
}
@Deprecated @Override
public String colNameString(String separator, String afterLast, boolean alias, ModelField... flds) {
return colNameString(Arrays.asList(flds), separator, afterLast, alias);
}
@Override
public StringBuilder colNameString(StringBuilder sb, String prefix, String separator, String afterLast, boolean alias, ModelField... flds) {
return colNameString(Arrays.asList(flds), sb, prefix, separator, afterLast, alias);
}
@Deprecated @Override
public String colNameString(List<ModelField> flds, String separator, String afterLast, boolean alias) {
return colNameString(flds, new StringBuilder(), "", separator, afterLast, alias).toString();
}
@Override
public StringBuilder colNameString(List<ModelField> flds, StringBuilder sb, String prefix, String separator, String afterLast, boolean alias) {
if (flds.size() < 1) {
return sb;
}
sb.append(prefix);
Iterator<ModelField> fldsIt = flds.iterator();
while (fldsIt.hasNext()) {
ModelField field = fldsIt.next();
sb.append(field.getColValue());
if (alias) {
ModelAlias modelAlias = this.getAlias(field.getName());
if (modelAlias != null) {
sb.append(" AS ").append(modelAlias.getColAlias());
}
}
if (fldsIt.hasNext()) {
sb.append(separator);
}
}
sb.append(afterLast);
return sb;
}
protected ModelEntity aliasedModelEntity = new ModelEntity();
public ModelEntity getAliasedModelEntity() {
return this.aliasedModelEntity;
}
public ModelEntity getAliasedEntity(String entityAlias, ModelReader modelReader) {
ModelMemberEntity modelMemberEntity = this.memberModelMemberEntities.get(entityAlias);
if (modelMemberEntity == null) {
Debug.logError("[" + this.getEntityName() + "]: No member entity with alias " + entityAlias + " found; this view-entity will NOT be usable...", module);
return null;
}
String aliasedEntityName = modelMemberEntity.getEntityName();
ModelEntity aliasedEntity = modelReader.getModelEntityNoCheck(aliasedEntityName);
if (aliasedEntity == null) {
Debug.logError("[" + this.getEntityName() + "]: [ModelViewEntity.populateFields] ERROR: could not find ModelEntity for entity name: " + aliasedEntityName, module);
return null;
}
return aliasedEntity;
}
public ModelField getAliasedField(ModelEntity aliasedEntity, String field, ModelReader modelReader) {
ModelField aliasedField = aliasedEntity.getField(field);
if (aliasedField == null) {
Debug.logError("[" + this.getEntityName() + "]: [ModelViewEntity.populateFields] ERROR: could not find ModelField for entity name: " + aliasedEntity.getEntityName() + " and field: " + field, module);
return null;
}
return aliasedField;
}
public void populateFields(ModelReader modelReader) {
populateFieldsBasic(modelReader);
populateReverseLinks();
}
public void populateFieldsBasic(ModelReader modelReader) {
for (Map.Entry<String, ModelMemberEntity> entry: memberModelMemberEntities.entrySet()) {
ModelMemberEntity modelMemberEntity = entry.getValue();
String aliasedEntityName = modelMemberEntity.getEntityName();
ModelEntity aliasedEntity = modelReader.getModelEntityNoCheck(aliasedEntityName);
if (aliasedEntity == null) {
continue;
}
memberModelEntities.put(entry.getKey(), aliasedEntityName);
Iterator<ModelField> aliasedFieldIterator = aliasedEntity.getFieldsIterator();
while (aliasedFieldIterator.hasNext()) {
ModelField aliasedModelField = aliasedFieldIterator.next();
ModelField newModelField = ModelField.create(this, aliasedModelField.getDescription(), modelMemberEntity.getEntityAlias() + "." + aliasedModelField.getName(),
aliasedModelField.getType(), modelMemberEntity.getEntityAlias() + "." + aliasedModelField.getColName(), null, null, false, false, false, false,
false, aliasedModelField.getValidators());
aliasedModelEntity.addField(newModelField);
}
}
expandAllAliasAlls(modelReader);
for (ModelAlias alias: aliases) {
// show a warning if function is specified and groupBy is true
if (UtilValidate.isNotEmpty(alias.function) && alias.groupBy) {
Debug.logWarning("[" + this.getEntityName() + "]: The view-entity alias with name=" + alias.name + " has a function value and is specified as a group-by field; this may be an error, but is not necessarily.", module);
}
String description = alias.description;
String name = alias.name;
String type = "";
String colName = "";
String colValue = "";
String fieldSet = "";
boolean isNotNull = false;
boolean isPk = false;
ModelField.EncryptMethod encryptMethod = ModelField.EncryptMethod.FALSE;
boolean isAutoCreatedInternal = false;
boolean enableAuditLog = false;
List<String> validators = null;
if (alias.isComplexAlias()) {
// if this is a complex alias, make a complex column name...
StringBuilder colNameBuffer = new StringBuilder();
StringBuilder fieldTypeBuffer = new StringBuilder();
alias.makeAliasColName(colNameBuffer, fieldTypeBuffer, this, modelReader);
colValue = colNameBuffer.toString();
colName = ModelUtil.javaNameToDbName(alias.name);
type = fieldTypeBuffer.toString();
isPk = false;
fieldSet = alias.getFieldSet();
} else {
ModelEntity aliasedEntity = getAliasedEntity(alias.entityAlias, modelReader);
ModelField aliasedField = getAliasedField(aliasedEntity, alias.field, modelReader);
if (aliasedField == null) {
Debug.logError("[" + this.getEntityName() + "]: [populateFields] ERROR: could not find ModelField for field name \"" +
alias.field + "\" on entity with name: " + aliasedEntity.getEntityName(), module);
continue;
}
if (alias.isPk != null) {
isPk = alias.isPk.booleanValue();
} else {
isPk = aliasedField.getIsPk();
}
encryptMethod = aliasedField.getEncryptMethod();
type = aliasedField.getType();
validators = aliasedField.getValidators();
colValue = alias.entityAlias + "." + SqlJdbcUtil.filterColName(aliasedField.getColName());
colName = SqlJdbcUtil.filterColName(colValue);
if (description.isEmpty()) {
description = aliasedField.getDescription();
}
if (alias.getFieldSet().isEmpty()) {
String aliasedFieldSet = aliasedField.getFieldSet();
if (!aliasedFieldSet.isEmpty()) {
StringBuilder fieldSetBuffer = new StringBuilder(alias.entityAlias);
fieldSetBuffer.append("_");
fieldSetBuffer.append(Character.toUpperCase(aliasedFieldSet.charAt(0)));
fieldSetBuffer.append(aliasedFieldSet.substring(1));
fieldSet = fieldSetBuffer.toString().intern();
Debug.logInfo("[" + this.getEntityName() + "]: copied field set on [" + name + "]: " + fieldSet, module);
}
} else {
fieldSet = alias.getFieldSet();
}
}
if (numericFunctionsSet.contains(alias.function)) {
// if we have a numeric function we have to change the type
type = "numeric";
}
if (UtilValidate.isNotEmpty(alias.function)) {
String prefix = functionPrefixMap.get(alias.function);
if (prefix == null) {
Debug.logWarning("[" + this.getEntityName() + "]: Specified alias function [" + alias.function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
} else {
colValue = prefix + colValue + ")";
}
}
ModelField field = ModelField.create(this, description, name, type, colName, colValue, fieldSet, isNotNull, isPk, encryptMethod, isAutoCreatedInternal, enableAuditLog, validators);
// if this is a groupBy field, add it to the groupBys list
if (alias.groupBy || groupByFields.contains(alias.name)) {
this.groupBys.add(field);
}
addField(field);
}
}
protected ModelConversion getOrCreateModelConversion(String aliasName) {
ModelEntity member = getMemberModelEntity(aliasName);
if (member == null) {
String errMsg = "No member found for aliasName - " + aliasName;
Debug.logWarning("[" + this.getEntityName() + "]: " + errMsg, module);
throw new RuntimeException("[" + this.getEntityName() + "]: Cannot create View Entity: " + errMsg);
}
ModelConversion[] allConversions = conversions.get(member.getEntityName());
if (allConversions == null) {
ModelConversion conversion = new ModelConversion(aliasName, member);
conversions.put(member.getEntityName(), new ModelConversion[] {conversion});
return conversion;
}
for (ModelConversion conversion: allConversions) {
if (conversion.aliasName.equals(aliasName)) {
return conversion;
}
}
ModelConversion[] newConversions = new ModelConversion[allConversions.length + 1];
System.arraycopy(allConversions, 0, newConversions, 0, allConversions.length);
ModelConversion conversion = new ModelConversion(aliasName, member);
newConversions[allConversions.length] = conversion;
conversions.put(member.getEntityName(), newConversions);
return conversion;
}
public void populateReverseLinks() {
Map<String, List<String>> containedModelFields = new HashMap<String, List<String>>();
Iterator<ModelAlias> it = getAliasesIterator();
while (it.hasNext()) {
ModelViewEntity.ModelAlias alias = it.next();
if (alias.isComplexAlias()) {
// TODO: conversion for complex-alias needs to be implemented for cache and in-memory eval stuff to work correctly
Debug.logWarning("[" + this.getEntityName() + "]: Conversion for complex-alias needs to be implemented for cache and in-memory eval stuff to work correctly, will not work for alias: " + alias.getName(), module);
} else {
ModelConversion conversion = getOrCreateModelConversion(alias.getEntityAlias());
conversion.addConversion(alias.getField(), alias.getName());
}
List<String> aliases = containedModelFields.get(alias.getField());
if (aliases == null) {
aliases = new LinkedList<String>();
containedModelFields.put(alias.getField(), aliases);
}
aliases.add(alias.getName());
}
Iterator<ModelViewLink> it2 = getViewLinksIterator();
while (it2.hasNext()) {
ModelViewEntity.ModelViewLink link = it2.next();
String leftAlias = link.getEntityAlias();
String rightAlias = link.getRelEntityAlias();
ModelConversion leftConversion = getOrCreateModelConversion(leftAlias);
ModelConversion rightConversion = getOrCreateModelConversion(rightAlias);
Iterator<ModelKeyMap> it3 = link.getKeyMapsIterator();
Debug.logVerbose(leftAlias + "<->" + rightAlias, module);
while (it3.hasNext()) {
ModelKeyMap mkm = it3.next();
String leftFieldName = mkm.getFieldName();
String rightFieldName = mkm.getRelFieldName();
rightConversion.addAllAliasConversions(containedModelFields.get(leftFieldName), rightFieldName);
leftConversion.addAllAliasConversions(containedModelFields.get(rightFieldName), leftFieldName);
}
}
int[] currentIndex = new int[conversions.size()];
int[] maxIndex = new int[conversions.size()];
ModelConversion[][] allConversions = new ModelConversion[conversions.size()][];
int i = 0;
for (ModelConversion[] aliasConversions: conversions.values()) {
currentIndex[i] = 0;
maxIndex[i] = aliasConversions.length;
allConversions[i] = new ModelConversion[aliasConversions.length];
System.arraycopy(aliasConversions, 0, allConversions[i], 0, aliasConversions.length);
i++;
}
int ptr = 0;
ModelConversion[] currentConversions = new ModelConversion[conversions.size()];
for (int j = 0, k; j < currentIndex.length; j++) {
for (int l = 0; l < maxIndex[ j ]; l++) {
while (true) {
for (i = 0, k = 0; i < currentIndex.length; i++) {
if (i == j && currentIndex[i] == l) continue;
currentConversions[k++] = allConversions[i][currentIndex[i]];
}
Debug.logVerbose(j + "," + l + ":" + Arrays.asList(currentConversions), module);
while (ptr < currentIndex.length && ++currentIndex[ptr] == maxIndex[ptr]) {
currentIndex[ptr] = 0;
ptr++;
}
if (ptr == currentIndex.length) break;
ptr = 0;
}
}
}
Debug.logVerbose(this + ":" + conversions, module);
}
public List<Map<String, Object>> convert(String fromEntityName, Map<String, ? extends Object> data) {
ModelConversion[] conversions = this.conversions.get(fromEntityName);
if (conversions == null) return null;
List<Map<String, Object>> values = new LinkedList<Map<String, Object>>();
for (ModelConversion conversion: conversions) {
conversion.convert(values, data);
}
return values;
}
/**
* Go through all aliasAlls and create an alias for each field of each member entity
*/
private void expandAllAliasAlls(ModelReader modelReader) {
for (ModelAliasAll aliasAll: aliasAlls) {
String entityAlias = aliasAll.getEntityAlias();
String prefix = aliasAll.getPrefix();
String function = aliasAll.getFunction();
boolean groupBy = aliasAll.getGroupBy();
String aliasAllFieldSet = aliasAll.getFieldSet();
ModelMemberEntity modelMemberEntity = memberModelMemberEntities.get(entityAlias);
if (modelMemberEntity == null) {
Debug.logError("[" + this.getEntityName() + "]: Member entity referred to in alias-all not found, ignoring: " + entityAlias, module);
continue;
}
String aliasedEntityName = modelMemberEntity.getEntityName();
ModelEntity aliasedEntity = modelReader.getModelEntityNoCheck(aliasedEntityName);
if (aliasedEntity == null) {
Debug.logError("[" + this.getEntityName() + "]: Entity referred to in member-entity " + entityAlias + " not found, ignoring: " + aliasedEntityName, module);
continue;
}
List<String> entFieldList = aliasedEntity.getAllFieldNames();
if (entFieldList == null) {
Debug.logError("[" + this.getEntityName() + "]: Entity referred to in member-entity " + entityAlias + " has no fields, ignoring: " + aliasedEntityName, module);
continue;
}
for (String fieldName: entFieldList) {
// now merge the lists, leaving out any that duplicate an existing alias name
String aliasName = fieldName;
ModelField modelField = aliasedEntity.getField(fieldName);
if (modelField.getIsAutoCreatedInternal()) {
// never auto-alias these
continue;
}
if (aliasAll.shouldExclude(fieldName)) {
// if specified as excluded, leave it out
continue;
}
if (UtilValidate.isNotEmpty(prefix)) {
StringBuilder newAliasBuffer = new StringBuilder(prefix);
//make sure the first letter is uppercase to delineate the field name
newAliasBuffer.append(Character.toUpperCase(aliasName.charAt(0)));
newAliasBuffer.append(aliasName.substring(1));
aliasName = newAliasBuffer.toString();
}
String fieldSet;
if (UtilValidate.isEmpty(aliasAllFieldSet)) {
String aliasedFieldSet = modelField.getFieldSet();
if (UtilValidate.isNotEmpty(aliasedFieldSet)) {
StringBuilder fieldSetBuffer = new StringBuilder(entityAlias);
if (UtilValidate.isNotEmpty(prefix)) {
fieldSetBuffer.append(Character.toUpperCase(prefix.charAt(0)));
fieldSetBuffer.append(prefix.substring(1));
}
fieldSetBuffer.append(Character.toUpperCase(aliasedFieldSet.charAt(0)));
fieldSetBuffer.append(aliasedFieldSet.substring(1));
fieldSet = fieldSetBuffer.toString();
} else {
fieldSet = "";
}
} else {
fieldSet = aliasAllFieldSet;
}
if (UtilValidate.isNotEmpty(fieldSet)) {
Debug.logInfo("[" + this.getEntityName() + "]: set field-set on [" + aliasName + "]: " + fieldSet, module);
}
ModelAlias existingAlias = this.getAlias(aliasName);
if (existingAlias != null) {
//log differently if this is part of a view-link key-map because that is a common case when a field will be auto-expanded multiple times
boolean isInViewLink = false;
Iterator<ModelViewLink> viewLinkIter = this.getViewLinksIterator();
while (viewLinkIter.hasNext() && !isInViewLink) {
ModelViewLink modelViewLink = viewLinkIter.next();
boolean isRel = false;
if (modelViewLink.getRelEntityAlias().equals(aliasAll.getEntityAlias())) {
isRel = true;
} else if (!modelViewLink.getEntityAlias().equals(aliasAll.getEntityAlias())) {
// not the rel-entity-alias or the entity-alias, so move along
continue;
}
Iterator<ModelKeyMap> keyMapIter = modelViewLink.getKeyMapsIterator();
while (keyMapIter.hasNext() && !isInViewLink) {
ModelKeyMap modelKeyMap = keyMapIter.next();
if (!isRel && modelKeyMap.getFieldName().equals(fieldName)) {
isInViewLink = true;
} else if (isRel && modelKeyMap.getRelFieldName().equals(fieldName)) {
isInViewLink = true;
}
}
}
//already exists, oh well... probably an override, but log just in case
String warnMsg = "[" + this.getEntityName() + "]: Throwing out field alias in view entity because one already exists with the alias name [" + aliasName + "] and field name [" + modelMemberEntity.getEntityAlias() + "(" + aliasedEntity.getEntityName() + ")." + fieldName + "], existing field name is [" + existingAlias.getEntityAlias() + "." + existingAlias.getField() + "]";
if (isInViewLink) {
Debug.logVerbose(warnMsg, module);
} else {
Debug.logInfo(warnMsg, module);
}
continue;
}
ModelAlias expandedAlias = new ModelAlias(aliasAll.getEntityAlias(), aliasName, fieldName, ModelUtil.javaNameToDbName(UtilXml.checkEmpty(aliasName)), null, groupBy, function, fieldSet, true);
expandedAlias.setDescription(modelField.getDescription());
aliases.add(expandedAlias);
}
}
}
@Override
public String toString() {
return "ModelViewEntity[" + getEntityName() + "]";
}
public static final class ModelMemberEntity implements Serializable {
protected final String entityAlias;
protected final String entityName;
public ModelMemberEntity(String entityAlias, String entityName) {
this.entityAlias = entityAlias;
this.entityName = entityName;
}
public String getEntityAlias() {
return this.entityAlias;
}
public String getEntityName() {
return this.entityName;
}
}
public static final class ModelAliasAll implements Serializable, Iterable<String> {
protected final String entityAlias;
protected final String prefix;
protected final Set<String> fieldsToExclude;
protected final boolean groupBy;
// is specified this alias is a calculated value; can be: min, max, sum, avg, count, count-distinct
protected final String function;
protected final String fieldSet;
@Deprecated
public ModelAliasAll(String entityAlias, String prefix) {
this(entityAlias, prefix, false, null, null, null);
}
@Deprecated
public ModelAliasAll(String entityAlias, String prefix, boolean groupBy, String function, Collection<String> excludes) {
this(entityAlias, prefix, groupBy, function, null, excludes);
}
public ModelAliasAll(String entityAlias, String prefix, boolean groupBy, String function, String fieldSet, Collection<String> excludes) {
this.entityAlias = entityAlias;
this.prefix = prefix;
this.groupBy = groupBy;
this.function = function;
this.fieldSet = fieldSet;
if (UtilValidate.isNotEmpty(excludes)) {
this.fieldsToExclude = new HashSet<String>(excludes.size());
this.fieldsToExclude.addAll(excludes);
} else {
this.fieldsToExclude = null;
}
}
public ModelAliasAll(Element aliasAllElement) {
this.entityAlias = UtilXml.checkEmpty(aliasAllElement.getAttribute("entity-alias")).intern();
this.prefix = UtilXml.checkEmpty(aliasAllElement.getAttribute("prefix")).intern();
this.groupBy = "true".equals(UtilXml.checkEmpty(aliasAllElement.getAttribute("group-by")));
this.function = UtilXml.checkEmpty(aliasAllElement.getAttribute("function"));
this.fieldSet = UtilXml.checkEmpty(aliasAllElement.getAttribute("field-set")).intern();
List<? extends Element> excludes = UtilXml.childElementList(aliasAllElement, "exclude");
if (UtilValidate.isNotEmpty(excludes)) {
this.fieldsToExclude = new HashSet<String>();
for (Element excludeElement: excludes) {
this.fieldsToExclude.add(excludeElement.getAttribute("field").intern());
}
} else {
this.fieldsToExclude = null;
}
}
public String getEntityAlias() {
return this.entityAlias;
}
public String getPrefix() {
return this.prefix;
}
public boolean getGroupBy() {
return this.groupBy;
}
public String getFunction() {
return this.function;
}
public String getFieldSet() {
return this.fieldSet;
}
public boolean shouldExclude(String fieldName) {
if (this.fieldsToExclude == null) {
return false;
} else {
return this.fieldsToExclude.contains(fieldName);
}
}
public Iterator<String> iterator() {
if (this.fieldsToExclude == null) {
return Collections.<String>emptySet().iterator();
} else {
return fieldsToExclude.iterator();
}
}
}
public static final class ModelAlias implements Serializable {
protected final String entityAlias;
protected final String name;
protected final String field;
protected final String colAlias;
// this is a Boolean object for a tri-state: null, true or false
protected final Boolean isPk;
protected final boolean groupBy;
// is specified this alias is a calculated value; can be: min, max, sum, avg, count, count-distinct
protected final String function;
protected final String fieldSet;
protected final boolean isFromAliasAll;
protected ComplexAliasMember complexAliasMember;
// The description for documentation purposes
protected String description = "";
public ModelAlias(Element aliasElement) {
this.entityAlias = UtilXml.checkEmpty(aliasElement.getAttribute("entity-alias")).intern();
this.name = UtilXml.checkEmpty(aliasElement.getAttribute("name")).intern();
this.field = UtilXml.checkEmpty(aliasElement.getAttribute("field"), this.name).intern();
this.colAlias = UtilXml.checkEmpty(aliasElement.getAttribute("col-alias"), ModelUtil.javaNameToDbName(UtilXml.checkEmpty(this.name))).intern();
String primKeyValue = UtilXml.checkEmpty(aliasElement.getAttribute("prim-key"));
if (UtilValidate.isNotEmpty(primKeyValue)) {
this.isPk = Boolean.valueOf("true".equals(primKeyValue));
} else {
this.isPk = null;
}
this.groupBy = "true".equals(UtilXml.checkEmpty(aliasElement.getAttribute("group-by")));
this.function = UtilXml.checkEmpty(aliasElement.getAttribute("function")).intern();
this.fieldSet = UtilXml.checkEmpty(aliasElement.getAttribute("field-set")).intern();
this.isFromAliasAll = false;
this.description = UtilXml.checkEmpty(UtilXml.childElementValue(aliasElement, "description")).intern();
Element complexAliasElement = UtilXml.firstChildElement(aliasElement, "complex-alias");
if (complexAliasElement != null) {
complexAliasMember = new ComplexAlias(complexAliasElement);
}
}
@Deprecated
public ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function) {
this(entityAlias, name, field, colAlias, isPk, groupBy, function, null, false);
}
public ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function, String fieldSet) {
this(entityAlias, name, field, colAlias, isPk, groupBy, function, fieldSet, false);
}
protected ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function, String fieldSet, boolean isFromAliasAll) {
this.entityAlias = entityAlias;
this.name = name;
this.field = UtilXml.checkEmpty(field, this.name);
this.colAlias = UtilXml.checkEmpty(colAlias, ModelUtil.javaNameToDbName(UtilXml.checkEmpty(this.name)));
this.isPk = isPk;
if (groupBy != null) {
this.groupBy = groupBy.booleanValue();
} else {
this.groupBy = false;
}
this.function = function;
this.fieldSet = UtilXml.checkEmpty(fieldSet).intern();
this.isFromAliasAll = isFromAliasAll;
}
public void setComplexAliasMember(ComplexAliasMember complexAliasMember) {
this.complexAliasMember = complexAliasMember;
}
public boolean isComplexAlias() {
return complexAliasMember != null;
}
public void makeAliasColName(StringBuilder colNameBuffer, StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader) {
if (complexAliasMember != null) {
complexAliasMember.makeAliasColName(colNameBuffer, fieldTypeBuffer, modelViewEntity, modelReader);
}
}
public String getEntityAlias() {
return this.entityAlias;
}
public String getName() {
return this.name;
}
public String getColAlias() {
return this.colAlias;
}
public String getField() {
return this.field;
}
public Boolean getIsPk() {
return this.isPk;
}
public boolean getGroupBy() {
return this.groupBy;
}
public String getFunction() {
return this.function;
}
public String getFieldSet() {
return fieldSet;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean getIsFromAliasAll() {
return this.isFromAliasAll;
}
}
public static interface ComplexAliasMember extends Serializable {
public void makeAliasColName(StringBuilder colNameBuffer, StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader);
}
public static final class ComplexAlias implements ComplexAliasMember {
protected final List<ComplexAliasMember> complexAliasMembers = new LinkedList<ComplexAliasMember>();
protected final String operator;
public ComplexAlias(String operator) {
this.operator = operator;
}
public ComplexAlias(Element complexAliasElement) {
this.operator = complexAliasElement.getAttribute("operator").intern();
// handle all complex-alias and complex-alias-field sub-elements
for (Element subElement: UtilXml.childElementList(complexAliasElement)) {
String nodeName = subElement.getNodeName();
if ("complex-alias".equals(nodeName)) {
this.addComplexAliasMember(new ComplexAlias(subElement));
} else if ("complex-alias-field".equals(nodeName)) {
this.addComplexAliasMember(new ComplexAliasField(subElement));
}
}
}
public void addComplexAliasMember(ComplexAliasMember complexAliasMember) {
this.complexAliasMembers.add(complexAliasMember);
}
public void addAllComplexAliasMembers(List<ComplexAliasMember> complexAliasMembers) {
this.complexAliasMembers.addAll(complexAliasMembers);
}
public void makeAliasColName(StringBuilder colNameBuffer, StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader) {
if (complexAliasMembers.size() == 0) {
return;
} else if (complexAliasMembers.size() == 1) {
ComplexAliasMember complexAliasMember = complexAliasMembers.iterator().next();
complexAliasMember.makeAliasColName(colNameBuffer, fieldTypeBuffer, modelViewEntity, modelReader);
} else {
colNameBuffer.append('(');
Iterator<ComplexAliasMember> complexAliasMemberIter = complexAliasMembers.iterator();
while (complexAliasMemberIter.hasNext()) {
ComplexAliasMember complexAliasMember = complexAliasMemberIter.next();
complexAliasMember.makeAliasColName(colNameBuffer, fieldTypeBuffer, modelViewEntity, modelReader);
if (complexAliasMemberIter.hasNext()) {
colNameBuffer.append(' ');
colNameBuffer.append(this.operator);
colNameBuffer.append(' ');
}
}
colNameBuffer.append(')');
}
}
}
public static final class ComplexAliasField implements ComplexAliasMember {
protected final String entityAlias;
protected final String field;
protected final String defaultValue;
protected final String function;
protected final String value;
public ComplexAliasField(Element complexAliasFieldElement) {
this.entityAlias = complexAliasFieldElement.getAttribute("entity-alias").intern();
this.field = complexAliasFieldElement.getAttribute("field").intern();
this.defaultValue = complexAliasFieldElement.getAttribute("default-value").intern();
this.function = complexAliasFieldElement.getAttribute("function").intern();
this.value = complexAliasFieldElement.getAttribute("value").intern();
}
public ComplexAliasField(String entityAlias, String field, String defaultValue, String function) {
this.entityAlias = entityAlias;
this.field = field;
this.defaultValue = defaultValue;
this.function = function;
this.value = null;
}
public ComplexAliasField(String entityAlias, String field, String defaultValue, String function, String value) {
this.entityAlias = entityAlias;
this.field = field;
this.defaultValue = defaultValue;
this.function = function;
this.value = value;
}
/**
* Make the alias as follows: function(coalesce(entityAlias.field, defaultValue))
*/
public void makeAliasColName(StringBuilder colNameBuffer, StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader) {
if(UtilValidate.isEmpty(entityAlias)
&& UtilValidate.isEmpty(field)
&& UtilValidate.isNotEmpty(value)){
colNameBuffer.append(value);
}
else {
ModelEntity modelEntity = modelViewEntity.getAliasedEntity(entityAlias, modelReader);
ModelField modelField = modelViewEntity.getAliasedField(modelEntity, field, modelReader);
String colName = entityAlias + "." + modelField.getColName();
if (UtilValidate.isNotEmpty(defaultValue)) {
colName = "COALESCE(" + colName + "," + defaultValue + ")";
}
if (UtilValidate.isNotEmpty(function)) {
String prefix = functionPrefixMap.get(function);
if (prefix == null) {
Debug.logWarning("[" + modelViewEntity.getEntityName() + "]: Specified alias function [" + function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
} else {
colName = prefix + colName + ")";
}
}
colNameBuffer.append(colName);
//set fieldTypeBuffer if not already set
if (fieldTypeBuffer.length() == 0) {
fieldTypeBuffer.append(modelField.getType());
}
}
}
}
public static final class ModelViewLink implements Serializable, Iterable<ModelKeyMap> {
protected final String entityAlias;
protected final String relEntityAlias;
protected final boolean relOptional;
protected final List<ModelKeyMap> keyMaps = new LinkedList<ModelKeyMap>();
protected final ViewEntityCondition viewEntityCondition;
public ModelViewLink(ModelViewEntity modelViewEntity, Element viewLinkElement) {
this.entityAlias = UtilXml.checkEmpty(viewLinkElement.getAttribute("entity-alias")).intern();
this.relEntityAlias = UtilXml.checkEmpty(viewLinkElement.getAttribute("rel-entity-alias")).intern();
// if anything but true will be false; ie defaults to false
this.relOptional = "true".equals(viewLinkElement.getAttribute("rel-optional"));
NodeList keyMapList = viewLinkElement.getElementsByTagName("key-map");
for (int j = 0; j < keyMapList.getLength(); j++) {
Element keyMapElement = (Element) keyMapList.item(j);
ModelKeyMap keyMap = new ModelKeyMap(keyMapElement);
if (keyMap != null) keyMaps.add(keyMap);
}
Element entityConditionElement = UtilXml.firstChildElement(viewLinkElement, "entity-condition");
if (entityConditionElement != null) {
this.viewEntityCondition = new ViewEntityCondition(modelViewEntity, this, entityConditionElement);
} else {
this.viewEntityCondition = null;
}
}
@Deprecated
public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ModelKeyMap... keyMaps) {
this(entityAlias, relEntityAlias, relOptional, null, Arrays.asList(keyMaps));
}
@Deprecated
public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, List<ModelKeyMap> keyMaps) {
this(entityAlias, relEntityAlias, relOptional, null, keyMaps);
}
public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ViewEntityCondition viewEntityCondition, ModelKeyMap... keyMaps) {
this(entityAlias, relEntityAlias, relOptional, viewEntityCondition, Arrays.asList(keyMaps));
}
public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ViewEntityCondition viewEntityCondition, List<ModelKeyMap> keyMaps) {
this.entityAlias = entityAlias;
this.relEntityAlias = relEntityAlias;
if (relOptional != null) {
this.relOptional = relOptional.booleanValue();
} else {
this.relOptional = false;
}
this.keyMaps.addAll(keyMaps);
this.viewEntityCondition = viewEntityCondition;
}
public String getEntityAlias() {
return this.entityAlias;
}
public String getRelEntityAlias() {
return this.relEntityAlias;
}
public boolean isRelOptional() {
return this.relOptional;
}
public ModelKeyMap getKeyMap(int index) {
return this.keyMaps.get(index);
}
public int getKeyMapsSize() {
return this.keyMaps.size();
}
public Iterator<ModelKeyMap> getKeyMapsIterator() {
return this.keyMaps.iterator();
}
public Iterator<ModelKeyMap> iterator() {
return this.keyMaps.iterator();
}
public List<ModelKeyMap> getKeyMapsCopy() {
List<ModelKeyMap> newList = new ArrayList<ModelKeyMap>(this.keyMaps);
return newList;
}
public ViewEntityCondition getViewEntityCondition() {
return this.viewEntityCondition;
}
}
public final class ModelConversion implements Serializable {
protected final String aliasName;
protected final ModelEntity fromModelEntity;
protected final Map<String, String> fieldMap = new HashMap<String, String>();
protected final Set<String> wildcards = new HashSet<String>();
public ModelConversion(String aliasName, ModelEntity fromModelEntity) {
this.aliasName = aliasName;
this.fromModelEntity = fromModelEntity;
Iterator<ModelField> it = getFieldsIterator();
while (it.hasNext()) {
ModelField field = it.next();
wildcards.add(field.getName());
}
}
@Override
public int hashCode() {
return fromModelEntity.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ModelConversion)) return false;
ModelConversion other = (ModelConversion) obj;
return fromModelEntity.equals(other.fromModelEntity);
}
public void addConversion(String fromFieldName, String toFieldName) {
wildcards.remove(toFieldName);
fieldMap.put(fromFieldName, toFieldName);
}
@Override
public String toString() {
//return fromModelEntity.getEntityName() + ":" + fieldMap + ":" + wildcards;
return aliasName + "(" + fromModelEntity.getEntityName() + ")";
}
public void convert(List<Map<String, Object>> values, Map<String, ? extends Object> value) {
Map<String, Object> newValue = new HashMap<String, Object>();
for (Map.Entry<String, String> entry: fieldMap.entrySet()) {
newValue.put(entry.getValue(), value.get(entry.getKey()));
}
for (String key: wildcards) {
newValue.put(key, EntityOperator.WILDCARD);
}
values.add(newValue);
}
public void addAllAliasConversions(String fieldName, String... aliases) {
addAllAliasConversions(Arrays.asList(aliases), fieldName);
}
public void addAllAliasConversions(List<String> aliases, String fieldName) {
if (aliases != null) {
for (String alias: aliases) {
addConversion(fieldName, alias);
}
}
}
}
public static final class ViewEntityCondition {
protected final ModelViewEntity modelViewEntity;
protected final ModelViewLink modelViewLink;
protected final boolean filterByDate;
protected final boolean distinct;
protected final List<String> orderByList;
protected final ViewCondition whereCondition;
protected final ViewCondition havingCondition;
// TODO: add programatic constructor
public ViewEntityCondition(ModelViewEntity modelViewEntity, ModelViewLink modelViewLink, Element element) {
this.modelViewEntity = modelViewEntity;
this.modelViewLink = modelViewLink;
this.filterByDate = "true".equals(element.getAttribute("filter-by-date"));
this.distinct = "true".equals(element.getAttribute("distinct"));
// process order-by
List<? extends Element> orderByElementList = UtilXml.childElementList(element, "order-by");
if (orderByElementList.size() > 0) {
orderByList = new ArrayList<String>(orderByElementList.size());
for (Element orderByElement: orderByElementList) {
orderByList.add(orderByElement.getAttribute("field-name"));
}
} else {
orderByList = null;
}
Element conditionExprElement = UtilXml.firstChildElement(element, "condition-expr");
Element conditionListElement = UtilXml.firstChildElement(element, "condition-list");
if (conditionExprElement != null) {
this.whereCondition = new ViewConditionExpr(this, conditionExprElement);
} else if (conditionListElement != null) {
this.whereCondition = new ViewConditionList(this, conditionListElement);
} else {
this.whereCondition = null;
}
Element havingConditionListElement = UtilXml.firstChildElement(element, "having-condition-list");
if (havingConditionListElement != null) {
this.havingCondition = new ViewConditionList(this, havingConditionListElement);
} else {
this.havingCondition = null;
}
}
public List<String> getOrderByList() {
return this.orderByList;
}
public EntityCondition getWhereCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
List<EntityCondition> conditionList = new LinkedList<EntityCondition>();
if(this.filterByDate) {
conditionList.add(EntityUtil.getFilterByDateExpr());
}
if (this.whereCondition != null) {
conditionList.add(whereCondition.createCondition(modelFieldTypeReader, entityAliasStack));
}
return EntityCondition.makeCondition(conditionList, EntityOperator.AND);
}
public EntityCondition getHavingCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
if (this.havingCondition != null) {
return this.havingCondition.createCondition(modelFieldTypeReader, entityAliasStack);
} else {
return null;
}
}
}
public static interface ViewCondition extends Serializable {
public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack);
}
public static final class ViewConditionExpr implements ViewCondition {
protected final ViewEntityCondition viewEntityCondition;
protected final String entityAlias;
protected final String fieldName;
protected final EntityComparisonOperator<?, ?> operator;
protected final String relEntityAlias;
protected final String relFieldName;
protected final Object value;
protected final boolean ignoreCase;
// TODO: add programatic constructor
public ViewConditionExpr(ViewEntityCondition viewEntityCondition, Element conditionExprElement) {
this.viewEntityCondition = viewEntityCondition;
String entityAlias = conditionExprElement.getAttribute("entity-alias");
this.fieldName = conditionExprElement.getAttribute("field-name");
String operator = UtilFormatOut.checkEmpty(conditionExprElement.getAttribute("operator"), "equals");
try {
this.operator = EntityOperator.lookupComparison(operator);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + operator);
}
String relEntityAlias = conditionExprElement.getAttribute("rel-entity-alias");
String relFieldNameStr = conditionExprElement.getAttribute("rel-field-name");
if (UtilValidate.isEmpty(relFieldNameStr)) {
this.relFieldName = null;
} else {
this.relFieldName = relFieldNameStr;
}
String valueStr = conditionExprElement.getAttribute("value");
if (UtilValidate.isEmpty(valueStr)) {
this.value = null;
} else {
this.value = valueStr;
}
this.ignoreCase = "true".equals(conditionExprElement.getAttribute("ignore-case"));
// if we are in a view-link, default to the entity-alias and rel-entity-alias there
if (this.viewEntityCondition.modelViewLink != null) {
if (UtilValidate.isEmpty(entityAlias)) {
entityAlias = this.viewEntityCondition.modelViewLink.getEntityAlias();
}
if (UtilValidate.isEmpty(relEntityAlias)) {
relEntityAlias = this.viewEntityCondition.modelViewLink.getRelEntityAlias();
}
}
this.entityAlias = entityAlias;
this.relEntityAlias = relEntityAlias;
}
public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
Object value = this.value;
// If IN or BETWEEN operator, see if value is a literal list and split it
if ((this.operator == EntityOperator.IN || this.operator == EntityOperator.BETWEEN)
&& value instanceof String) {
String delim = null;
if (((String)value).indexOf("|") >= 0) {
delim = "|";
} else if (((String)value).indexOf(",") >= 0) {
delim = ",";
}
if (UtilValidate.isNotEmpty(delim)) {
value = StringUtil.split((String) value, delim);
}
}
EntityConditionValue lhs = EntityFieldValue.makeFieldValue(this.fieldName, this.entityAlias, entityAliasStack, this.viewEntityCondition.modelViewEntity);
ModelField lhsField = lhs.getModelField(this.viewEntityCondition.modelViewEntity);
if (lhsField == null) {
throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Error in Entity Find: could not find field [" + fieldName + "]");
}
// don't convert the field to the desired type if this is an IN or BETWEEN operator and we have a Collection
if (!((this.operator == EntityOperator.IN || this.operator == EntityOperator.BETWEEN)
&& value instanceof Collection<?>)) {
// now to a type conversion for the target fieldName
value = this.viewEntityCondition.modelViewEntity.convertFieldValue(lhsField, value, modelFieldTypeReader, new HashMap<String, Object>());
}
if (Debug.verboseOn()) Debug.logVerbose("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Got value for fieldName [" + fieldName + "]: " + value, module);
Object rhs = null;
if (value != null) {
rhs = value;
} else {
rhs = EntityFieldValue.makeFieldValue(this.relFieldName, this.relEntityAlias, entityAliasStack, this.viewEntityCondition.modelViewEntity);
}
EntityCondition entityCondition;
if (this.operator == EntityOperator.NOT_EQUAL && value != null) {
// since some databases don't consider nulls in != comparisons, explicitly include them
// this makes more sense logically, but if anyone ever needs it to not behave this way we should add an "or-null" attribute that is true by default
if (ignoreCase) {
entityCondition = EntityCondition.makeCondition(
EntityCondition.makeCondition(EntityFunction.UPPER(lhs), this.operator, EntityFunction.UPPER(rhs)),
EntityOperator.OR,
EntityCondition.makeCondition(lhs, EntityOperator.EQUALS, null));
} else {
entityCondition = EntityCondition.makeCondition(
EntityCondition.makeCondition(lhs, this.operator, rhs),
EntityOperator.OR,
EntityCondition.makeCondition(lhs, EntityOperator.EQUALS, null));
}
} else if ( value == null && this.relFieldName == null && (this.operator == EntityOperator.EQUALS || this.operator == EntityOperator.NOT_EQUAL)) {
entityCondition = EntityCondition.makeCondition(lhs, this.operator, null);
} else {
if (ignoreCase) {
// use the stuff to upper case both sides
entityCondition = EntityCondition.makeCondition(EntityFunction.UPPER(lhs), this.operator, EntityFunction.UPPER(rhs));
} else {
entityCondition = EntityCondition.makeCondition(lhs, this.operator, rhs);
}
}
if(this.viewEntityCondition.filterByDate) {
List<EntityCondition> conditionList = new LinkedList<EntityCondition>();
conditionList.add(entityCondition);
conditionList.add(EntityUtil.getFilterByDateExpr());
return EntityCondition.makeCondition(conditionList, EntityOperator.AND);
} else {
return entityCondition;
}
}
}
public static final class ViewConditionList implements ViewCondition {
protected final ViewEntityCondition viewEntityCondition;
protected final List<ViewCondition> conditionList = new LinkedList<ViewCondition>();
protected final EntityJoinOperator operator;
public ViewConditionList(ViewEntityCondition viewEntityCondition, Element conditionListElement) {
this.viewEntityCondition = viewEntityCondition;
String combine = conditionListElement.getAttribute("combine");
try {
this.operator = EntityOperator.lookupJoin(combine);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + combine);
}
List<? extends Element> subElements = UtilXml.childElementList(conditionListElement);
for (Element subElement: subElements) {
if ("condition-expr".equals(subElement.getNodeName())) {
conditionList.add(new ViewConditionExpr(this.viewEntityCondition, subElement));
} else if ("condition-list".equals(subElement.getNodeName())) {
conditionList.add(new ViewConditionList(this.viewEntityCondition, subElement));
} else {
throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Invalid element with name [" + subElement.getNodeName() + "] found under a condition-list element.");
}
}
}
public ViewConditionList(ViewEntityCondition viewEntityCondition, String combine, List<ViewCondition> conditionList) {
this.viewEntityCondition = viewEntityCondition;
try {
this.operator = EntityOperator.lookupJoin(combine);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + combine);
}
if (UtilValidate.isNotEmpty(conditionList)) {
this.conditionList.addAll(conditionList);
}
}
public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
if (this.conditionList.size() == 0) {
return null;
}
if (this.conditionList.size() == 1) {
ViewCondition condition = this.conditionList.get(0);
return condition.createCondition(modelFieldTypeReader, entityAliasStack);
}
List<EntityCondition> entityConditionList = new LinkedList<EntityCondition>();
for (ViewCondition curCondition: conditionList) {
EntityCondition econd = curCondition.createCondition(modelFieldTypeReader, entityAliasStack);
if (econd != null) {
entityConditionList.add(econd);
}
}
if(this.viewEntityCondition.filterByDate) {
entityConditionList.add(EntityUtil.getFilterByDateExpr());
}
if(this.viewEntityCondition.filterByDate) {
entityConditionList.add(EntityUtil.getFilterByDateExpr());
}
return EntityCondition.makeCondition(entityConditionList, this.operator);
}
}
}