tools.xor.view.TraversalView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xor Show documentation
Show all versions of xor Show documentation
Empowering Model Driven Architecture in J2EE applications
/**
* XOR, empowering Model Driven Architecture in J2EE applications
*
* Copyright (c) 2012, Dilip Dalton
*
* Licensed 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 tools.xor.view;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.springframework.util.StringUtils;
import tools.xor.AbstractType;
import tools.xor.AggregateAction;
import tools.xor.AssociationSetting;
import tools.xor.EntityType;
import tools.xor.FunctionScope;
import tools.xor.FunctionType;
import tools.xor.MatchType;
import tools.xor.Settings;
import tools.xor.Type;
import tools.xor.service.Shape;
import tools.xor.util.ClassUtil;
import tools.xor.util.DFAtoRE;
import tools.xor.util.Edge;
import tools.xor.util.InterQuery;
import tools.xor.util.State;
import tools.xor.util.Vertex;
import tools.xor.util.graph.StateGraph;
import tools.xor.util.graph.StateTree;
import tools.xor.util.graph.TypeGraph;
import tools.xor.view.AggregateTree.QueryKey;
import tools.xor.view.expression.AliasHandler;
/**
*
Views are interpreted based on the following:
a.b.[VIEW_NAME] - would expand the properties list to include that in the VIEW_NAME
child view:
1. It has a generated name
2. Used to represent a separate query
3. Used to narrow a type
4. Specify relative properties
An alias cannot be specified without the anchor path
By default all properties are absolute paths
So with regards to the child view, we have 2 dimensions, and let us look at how they affect the child view functionality:
A ‘✓’ marks the presence of that dimension
These values are specified using the ALIAS function on the parent view.
If the alias refers to a view then the alias type is VIEW, if it refers to a property then the alias type is PROPERTY.
For a view alias, the view name is required, the view entity type or anchor path is optional.
The table below shows how the view alias is interpreted depending upon if the EntityType and/or the anchor path is provided.
child EntityType anchor path Outcome
=======================================================================================================
✓ ✓ Used for reasons (2), (3) and (4)
✓ Used for reasons (2) and (4)
Question: What if I want the child query to be part of the parent query and to have type narrowing on the child
Solution: A NARROW function needs to be created and a child view is not necessary
Question: An ALIAS function is provided, but the queries are not created using FragmentBuilder
Solution: We don’t have to create Fragments, but we have to have QueryField objects for each query
*/
@XmlRootElement(name="TraversalView")
public class TraversalView implements Comparable, Vertex, View {
private static final Logger logger = LogManager.getLogger(new Exception().getStackTrace()[0].getClassName());
public static final String VIEW_REFERENCE_START = "[";
public static final String VIEW_REFERENCE_NAME_REGEX = "^.*\\[\\s*(\\w+)\\s*\\].*";
public static final String VIEW_REFERENCE_MULTIPLE_REGEX = "^(.*)(\\[\\s*(\\w+)\\s*\\]).*(\\[\\s*\\w+\\s*\\].*)";
public static final String REGEX_STRING = "[\\*\\?\\+\\[\\{\\|\\(\\)\\^\\$]";
public static final Pattern REGEX_STRING_MATCHER = Pattern.compile(REGEX_STRING);
public static final String DOMAIN = "DOMAIN:";
public static final String REGEX = "_REGEX_";
protected String name;
protected String anchorPath;
protected String typeName; // represents the root entity type name
protected List join;
protected int version; // The version from which this view is effective
protected String dasName; // The DataAccessService name for which this view is applicable.
// Optional parameter useful for native queries
@XmlAttribute
protected Integer resultPosition; // Wrapper class, because we test custom
// The primary key attribute name needed for linking with child views
// This is a list because a primary key can be composite
protected List primaryKeyAttribute;
// A dotted notation list of attributes representing the view scope
protected List attributeList;
// Alternative representation using a richer format, i.e., JSON
protected String jsonString;
// A JSONObject of the jsonString
// With be built either from attributeList or jsonString
// jsonString has richer information and is the recommended approach
@XmlTransient
protected JSONObject json;
// Each function should be independent of one another
// TODO: Should this be pushed down to AggregateView?
protected List function = new ArrayList<>();
@XmlTransient
private boolean expanded;
@XmlTransient
private Set exactAttributes; // These do not have the recursive operand (*)
// and exact match can be performed
@XmlTransient
private Map regexAttributes;
@XmlTransient
private boolean isSplitToRoot = true;
@XmlTransient
private Shape shape; // The Shape with which this view is associated
@XmlTransient
private final Map queryCache = new ConcurrentHashMap<>();
// A view can be valid for multiple entity types, especially if the view refers
// to the attributes in a base class. Then in this case, the view can be
// used by all the subtypes of that base class.
@XmlTransient
private final Map>> stateGraph = new ConcurrentHashMap<>();
@XmlTransient
private final Map aliasMap = new ConcurrentHashMap<>();
@XmlTransient
private final Map viewAliasMap = new ConcurrentHashMap<>();
/********************** C O N S T R U C T O R S ***************************/
/**
* Construct a view with a corresponding QueryTree instance
* @param queryTree instance
*/
public TraversalView(QueryTree queryTree) {
initQuery(queryTree);
}
public TraversalView(Type type, String viewName) {
this(viewName);
this.typeName = type.getName();
}
public TraversalView(String viewName) {
setName(viewName);
}
/**
* No-args constructor required for Unmarshalling purpose and also internally by the framework.
* Don't use this directly.
*/
public TraversalView() {
// Name is required
setName(UUID.randomUUID().toString());
}
private void initQuery(QueryTree queryTree) {
this.name = queryTree.getName();
this.typeName = queryTree.getAggregateType().getName();
// We create the OQLQuery object and populate it with information from the query view
// such as ColumnMeta information
// The query builder should have populated the OQLQuery object
}
@Override
public List getPrimaryKeyAttribute () {
return this.primaryKeyAttribute;
}
@Override public boolean isTempTablePopulated ()
{
return false;
}
@Override public void setTempTablePopulated (boolean tempTablePopulated)
{
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@Override public Integer getResultPosition ()
{
return resultPosition;
}
public void setPrimaryKeyAttribute (List attribute) {
this.primaryKeyAttribute = attribute;
}
@Override
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
@Override
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Override
public List getJoin() {
return join;
}
public void setJoin(List join) {
this.join = join;
}
public void setDasName(String name) {
this.dasName = name;
}
public List getChildren() {
return null;
}
public void setChildren(List children) {
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@Override
public List getFunction () {
return function;
}
public void setFunction (List functions) {
this.function = functions;
}
public Function addFunction (FunctionType type, String arg) {
List args = new LinkedList<>();
args.add(arg);
return addFunction(null, type, 1, args);
}
public Function addFunction (String name, String arg1, String arg2) {
List args = new LinkedList<>();
args.add(arg1);
args.add(arg2);
return addFunction(name, FunctionType.COMPARISON, 1, args);
}
public Function addFunction (String name, String arg1, String arg2, String arg3) {
List args = new LinkedList<>();
args.add(arg1);
args.add(arg2);
args.add(arg3);
return addFunction(name, FunctionType.COMPARISON, 1, args);
}
public Function addFunction (FunctionType type, int position, String arg) {
List args = new LinkedList<>();
args.add(arg);
return addFunction(null, type, position, args);
}
public Function addFunction (String name, FunctionType type, int position, List args) {
// We prohibit directly adding condition by the user for security reasons
if(type == FunctionType.FREESTYLE) {
throw new IllegalStateException("Direct addition of query condition is prohibited from Settings");
}
Function newFunction = new Function(name, type, FunctionScope.ANY, position, args, null);
this.function.add(newFunction);
return newFunction;
}
public OQLQuery getSystemOQLQuery() {
return null;
}
public void setSystemOQLQuery(OQLQuery systemOQLQuery) {
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@Override
public OQLQuery getUserOQLQuery() {
return null;
}
@Override
public void setUserOQLQuery(OQLQuery userOQLQuery) {
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@Override
public NativeQuery getNativeQuery() {
return null;
}
public void setNativeQuery(NativeQuery nativeQuery) {
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@Override
public StoredProcedure getStoredProcedure(final AggregateAction action) {
return null;
}
@Override
public List getStoredProcedure() {
return null;
}
@Override
public void setStoredProcedure(List storedProcedure) {
throw new UnsupportedOperationException("This method cannot be invoked on TraversalView");
}
@XmlTransient
@Override
public Shape getShape() {
return this.shape;
}
@Override
public void setShape(Shape shape) {
this.shape = shape;
}
@Override
public List getConsolidatedAttributes () {
List result = new LinkedList<>();
if(getAttributeList() != null) {
result.addAll(getAttributeList());
}
if(getChildren() != null) {
for (View child : getChildren()) {
result.addAll(child.getConsolidatedAttributes());
}
}
return result;
}
@Override
public JSONObject getJson() {
return this.json;
}
@Override
public List getAttributeList() {
return attributeList;
}
@Override
public void setAttributeList(List attributeList) {
this.attributeList = attributeList;
}
@Override public String getTypeName() {
return typeName;
}
@Override
public void setTypeName(String typeName) {
this.typeName = typeName;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getAnchorPath() {
return anchorPath;
}
@Override
public void setAnchorPath(String path) {
this.anchorPath = path;
}
private AggregateTree getAggregateTree (QueryKey viewKey) {
if(!queryCache.containsKey(viewKey)) {
// Build the QueryTree
AggregateTree> aggregateTree = new AggregateTree(this);
new FragmentBuilder(aggregateTree).build((EntityType)viewKey.type);
// run through the cartesian join splitter
if (isSplitToRoot()) {
(new SplitToRoot(aggregateTree)).execute();
}
else {
(new SplitToAnchor(aggregateTree)).execute();
(new SplitSubtype(aggregateTree)).execute();
}
Shape shape = ((EntityType)viewKey.type).getShape();
(new JoinTableAmender(aggregateTree, shape)).execute();
queryCache.put(viewKey, aggregateTree);
}
return queryCache.get(viewKey).copy();
}
@Override
public AggregateTree getAggregateTree (Type type) {
return getAggregateTree(new QueryKey(type, name));
}
public static boolean isBuiltInView(String viewName) {
for(ViewType viewType: ViewType.values()) {
if(viewName.endsWith(viewType.toString())) {
return true;
}
}
return false;
}
public static boolean isAggregateView(String viewName) {
return viewName.endsWith(ViewType.AGGREGATE.toString());
}
@Override
public Class inferDomainClass() {
String suffix = null;
Class result = null;
for(ViewType viewType: ViewType.values()) {
if(name.endsWith(viewType.toString())) {
suffix = Settings.URI_PATH_DELIMITER + viewType.toString();
break;
}
}
if(suffix != null) {
String encodedName = name.substring(0, name.indexOf(suffix));
String className = Settings.decodeParam(encodedName);
try {
result = Class.forName(className);
}
catch (ClassNotFoundException e) {
result = null;
}
}
return result;
}
@Override
public TraversalView copy()
{
TraversalView copy = new TraversalView();
copyInto(copy);
return copy;
}
protected void copyInto(TraversalView copy) {
copy.setName(name);
copy.setAnchorPath(anchorPath);
copy.setTypeName(typeName);
copy.setExpanded(expanded);
copy.jsonString = jsonString;
copy.resultPosition = resultPosition;
if(json != null) {
copy.json = ClassUtil.copyJson(json);
}
copy.setSplitToRoot(isSplitToRoot());
if(attributeList != null) {
List attributesCopy = new ArrayList<>();
// XML format adds spaces at the end of the path that we don't need
for(String path: attributeList) {
attributesCopy.add(path.trim());
}
copy.setAttributeList(new ArrayList<>(attributesCopy));
}
if(primaryKeyAttribute != null) {
copy.primaryKeyAttribute = new ArrayList<>(primaryKeyAttribute);
}
if(function != null) {
List functionCopy = new ArrayList<>();
for(Function f: function) {
functionCopy.add(new Function(f));
}
copy.setFunction(functionCopy);
}
copy.setJoin(join);
if(regexAttributes != null) {
copy.regexAttributes = new HashMap(regexAttributes);
}
if(exactAttributes != null) {
copy.exactAttributes = new HashSet(exactAttributes);
}
// NOTE: If a copy is taken and the values changed then the state graph could become invalidated
if(stateGraph != null) {
for(Map.Entry>> entry: stateGraph.entrySet()) {
copy.stateGraph.put(entry.getKey(), entry.getValue().copy());
}
}
copy.setShape(getShape());
}
@Override
public Set getViewReferences() {
Set result = new HashSet<>();
for(String attribute: getConsolidatedAttributes()) {
String viewName = getViewReference(attribute);
if(viewName != null)
result.add(viewName);
}
return result;
}
public static String getViewReference(String attribute) {
if(attribute.matches(VIEW_REFERENCE_NAME_REGEX)) {
Scanner s = new Scanner(attribute);
try {
s.findInLine(VIEW_REFERENCE_NAME_REGEX);
MatchResult result = s.match();
if(result.groupCount() == 1)
return result.group(1);
} finally {
s.close();
}
}
return null;
}
@Override
public boolean hasViewReference() {
for (String attribute : getConsolidatedAttributes()) {
if (getViewReference(attribute) != null)
return true;
}
return false;
}
// Extract JSON from the attributes list
public JSONObject extractJSON(List expanding)
{
JSONObject result = new JSONObject();
if(jsonString != null && attributeList != null) {
throw new RuntimeException("Cannot configure view with both json and path representation. Choose one.");
}
if(jsonString != null) {
result = new JSONObject(jsonString);
} else if(attributeList != null) {
extractJSON(result, attributeList);
}
if(!isCompositionView()) {
expand(result, "", expanding);
}
return result;
}
// TODO: make private
public static void extractJSON(JSONObject json, List attributes) {
for(String path: attributes) {
String remaining = State.getRemaining(path);
String field = State.getNextAttr(path);
// create intermediate objects if needed
JSONObject owner = json;
while(remaining != null) {
if(!owner.has(field)) {
owner.put(field, new JSONObject());
}
owner = owner.getJSONObject(field);
field = State.getNextAttr(remaining);
remaining = State.getRemaining(remaining);
}
owner.put(field, "");
}
}
protected void addChildView(View view, String anchor) {
throw new UnsupportedOperationException("A TraversalView cannot have children");
}
private void expand(JSONObject owner, String anchor, List expanding) {
List toRemove = new LinkedList<>();
Map toAdd = new HashMap<>();
Iterator iter = owner.keys();
while(iter.hasNext()) {
String key = (String)iter.next();
String anchorPath = anchor + ((StringUtils.isEmpty(anchor) ? "" : Settings.PATH_DELIMITER) + key);
Object child = owner.get(key);
if(child instanceof JSONObject) {
expand((JSONObject) child, anchorPath, expanding);
} else {
JSONObject reference = null;
boolean isValidReference = false;
if (getViewReference(key) != null) {
View view = getView(getShape(), key);
if(view != null) {
isValidReference = true;
// This will become a child AggregateView, since it is targeted for querying
if(view.isCustom()) {
addChildView(view, anchorPath);
} else {
view.expand(expanding);
reference = view.getJson();
}
}
}
if(isValidReference) {
// remove the view reference field
toRemove.add(key);
if(reference != null) {
// merge the reference object with the owner
Iterator childIter = reference.keys();
while (childIter.hasNext()) {
String childKey = (String)childIter.next();
toAdd.put(childKey, reference.get(childKey));
}
}
}
}
}
for(String key: toRemove) {
owner.remove(key);
}
for(Map.Entry entry: toAdd.entrySet()) {
owner.put(entry.getKey(), entry.getValue());
}
}
public boolean isCustom() {
return false;
}
@Override
public boolean isCompositionView() {
if(getAttributeList() != null) {
for (String attribute : getAttributeList()) {
if (isCompositionReference(attribute)) {
View view = getView(shape, attribute);
if(view.isCustom()) {
return true;
}
}
}
}
return false;
}
public static boolean isCompositionReference(String path) {
if(path.trim().startsWith(TraversalView.VIEW_REFERENCE_START)) {
return true;
}
return false;
}
// TODO: A view is expanded for 2 reasons
// 1. QUERY - to be used in a query
// Any user provided child queries (or view references) should not be expanded
// 2. TRAVERSAL - to be used for either update operations or read by graph traversal
@Override
public void expand() {
this.expand(new LinkedList<>());
}
@Override
public void expand(List expanding) {
// If it is already expanded then return
if(isExpanded()) {
return;
}
if(expanding.contains(getName())) {
throw new RuntimeException("Cyclic view reference detected: " + String.join(" -> ", expanding));
} else {
expanding.add(getName());
}
this.json = extractJSON(expanding);
// We don't expand composition views
if(!isCompositionView()) {
// Find and substitute the view references
// TODO: save a copy of the original attributeList to help with identifiying if
// it is expandable
attributeList = getExpandedList(getAttributeList(), expanding);
// Get the RegEx attributes
Map regexMap = new HashMap<>();
Set exactSet = new HashSet<>();
for (String attrPath : this.attributeList) {
if (DFAtoRE.isRegex(attrPath)) {
regexMap.put(attrPath, Pattern.compile(attrPath));
}
else {
exactSet.add(attrPath);
}
}
if (regexMap.size() > 0) {
this.setRegexAttributes(regexMap);
}
if (exactSet.size() > 0) {
this.setExactAttributes(exactSet);
}
}
setExpanded(true);
}
private boolean hasRegex() {
if(this.attributeList != null) {
for (String attrPath : this.attributeList) {
if (DFAtoRE.isRegex(attrPath)) {
return true;
}
}
}
return false;
}
@Override
public List getExpandedList(List input, List expanding) {
// Find and substitute the view references
List newList = new ArrayList<>();
if(input != null) {
for (String attribute : input) {
if (getViewReference(attribute) != null) {
newList.addAll(expand(getShape(), attribute, expanding));
}
else {
newList.add(attribute);
}
}
}
return newList;
}
@Override
public Set getFunctionAttributes() {
Set functionAttributes = new HashSet<>();
if(getFunction() != null) {
for (Function function : getFunction()) {
functionAttributes.addAll(function.getAttributes());
}
}
return functionAttributes;
}
private static View getView(Shape shape, String attribute) {
if(attribute.matches(VIEW_REFERENCE_MULTIPLE_REGEX)) {
throw new IllegalStateException("Cannot refer to more than one view in an attribute: " + attribute);
}
String viewName = getViewReference(attribute);
View view = shape.getView(viewName);
return view;
}
private static List expand(Shape shape, String attribute, List expanding) {
View view = getView(shape, attribute);
// Attributes might be referring to a RegEx expression
if(view == null) {
List unchanged = new LinkedList<>();
unchanged.add(attribute);
return unchanged;
}
if(!view.isExpanded()) {
view.expand(expanding);
}
String prefix = extractAnchor(attribute);
List expandedAttributes = new ArrayList<>();
if(view.isCustom()) {
List pkAttribute = view.getPrimaryKeyAttribute();
if(pkAttribute == null) {
throw new RuntimeException("primaryKeyAttribute needs to be specified to use a custom view. " +
"This is needed for efficieny reasons as the whole point of the custom view is to avoid inefficient fetching by the parent");
}
for (String suffix : pkAttribute) {
expandedAttributes.add(prefix + suffix);
}
} else {
for (String suffix : view.getAttributeList()) {
expandedAttributes.add(prefix + suffix);
}
}
return expandedAttributes;
}
@XmlTransient
@Override
public Set getExactAttributes ()
{
return exactAttributes;
}
public void setExactAttributes (Set exactAttributes)
{
this.exactAttributes = exactAttributes;
}
@XmlTransient
@Override
public Map getRegexAttributes() {
return this.regexAttributes;
}
public void setRegexAttributes(Map regexAttributes) {
this.regexAttributes = regexAttributes;
}
@Override
public boolean matches(String path) {
if(regexAttributes != null) {
for(Pattern p: regexAttributes.values()) {
Matcher matcher = p.matcher(path);
if(matcher.matches() || matcher.hitEnd()) {
return true;
}
}
}
return false;
}
@XmlTransient
public Map>> getStateGraph() {
return this.stateGraph;
}
private String getEntityName(EntityType type, StateGraph.Scope scope) {
String namePrefix = scope.name() + ":";
if(type.isDomainType()) {
return namePrefix + DOMAIN + type.getName();
} else {
return namePrefix + type.getName();
}
}
@Override
public void addTypeGraph (EntityType type, TypeGraph> value, StateGraph.Scope scope) {
stateGraph.put(getEntityName(type, scope), (StateGraph>)value);
}
@Override
public TypeGraph> getTypeGraph (EntityType entityType) {
return getTypeGraph(entityType, StateGraph.Scope.TYPE_GRAPH);
}
public TypeGraph> getTypeGraph () {
if(typeName == null) {
throw new IllegalStateException("The type for the view needs to be provided");
}
// Return the type graph related to the view EntityType
return getTypeGraph(null);
}
@Override
public boolean isTree (Settings settings) {
if(settings.getExpandedAssociations() != null && settings.getExpandedAssociations().size() > 0) {
for(AssociationSetting assoc: settings.getExpandedAssociations()) {
if(assoc.getMatchType() == MatchType.TYPE ||
assoc.getMatchType() == MatchType.RELATIVE_PATH) {
return false;
}
}
}
if(isAggregateView(getName())) {
return false;
}
if(hasRegex()) {
return false;
}
return true;
}
@Override
public TypeGraph> getTypeGraph (EntityType entityType, StateGraph.Scope scope) {
// If entityType is provided, it takes precedence over typeName
// Ensure entityType is same or subType of typeName
EntityType type = null;
if(typeName != null) {
type = (EntityType)shape.getType(typeName);
if(entityType != null) {
assert type.isSameOrSupertypeOf(entityType) :
"EntityType should be of the same type as " + typeName;
} else {
entityType = type;
}
}
String entityName = getEntityName(entityType, scope);
// This is not a default view, then we need to construct the type graph for this view
if(!stateGraph.containsKey(entityName) ) {
if(typeName != null) {
// If EntityType is not provided, then use type as the EntityType
if(entityType == null && type instanceof EntityType) {
entityType = (EntityType) type;
}
if(entityType == null) {
throw new RuntimeException("The given type should be an entityType: " + typeName);
}
if (!type.isSameOrSupertypeOf(entityType)) {
throw new RuntimeException(
"The view type " + type.getName()
+ " should either be the same or a supertype of the given type: "
+ entityType.getName());
}
}
switch(scope) {
case EDGE:
stateGraph.put(entityName, StateTree.build(this, entityType));
break;
case VIEW_GRAPH:
stateGraph.put(entityName, DFAtoRE.build(this, entityType));
break;
case TYPE_GRAPH:
DFAtoRE dfaRE = new DFAtoRE(entityType, shape);
stateGraph.put(entityName, dfaRE.getExactStateGraph());
break;
case FULL_GRAPH:
dfaRE = new DFAtoRE(entityType, shape);
stateGraph.put(entityName, dfaRE.getFullStateGraph());
break;
}
}
return stateGraph.get(entityName);
}
public static boolean isEdgeGraph(View view) {
return !view.isExpanded() && view.getTypeName() != null;
}
@Override
public int compareTo(TraversalView o) {
return (name.equals(o.getName())) ? (version - o.getVersion()) : name.compareTo(o.getName());
}
public boolean isMigrateView(Type type) {
String migrateViewName = AbstractType.getMigrateViewName(type);
return migrateViewName != null && migrateViewName.equals(getName());
}
@Override
public boolean isValid() {
TypeGraph typeGraph = getTypeGraph();
for(String path: attributeList) {
if(!typeGraph.hasPath(path)) {
logger.info("Invalid path: " + path);
return false;
}
}
return true;
}
/**
* propertyName - optional. If the propertyName is not provided then it represents an alias on the root object
* alias - required
* typeName - alias type, can be a subtype of the type of propertyName
* viewName - optional, represents the properties added to the alias
*/
public static class PropertyAlias {
String alias;
String propertyName;
String typeName;
String viewName; // Could give rise to an interquery edge if the view is a custom view
String elementType;
public PropertyAlias(String alias, String propertyName, String typeName, String viewName) {
this.alias = alias;
this.propertyName = propertyName;
this.typeName = typeName;
this.viewName = viewName;
assert(this.alias != null);
}
public PropertyAlias(String alias, String propertyName, String typeName, String viewName, String elementType) {
this(alias, propertyName, typeName, viewName);
this.elementType = elementType;
}
public boolean isViewReference() {
return this.viewName != null && !"".equals(this.viewName.trim());
}
public String getViewName() {
return this.viewName;
}
public String getTypeName () {
return this.typeName;
}
public String getAlias() {
return this.alias;
}
public String getOriginal() {
return Settings.getBaseName(this.propertyName);
}
public String getElementType() {
return elementType;
}
public void setElementType(String elementType) {
this.elementType = elementType;
}
@Override
public boolean equals(Object o) {
if(this == o)
return true;
if(!PropertyAlias.class.isAssignableFrom(o.getClass()))
return false;
PropertyAlias other = (PropertyAlias) o;
if(!alias.equals(other.alias)) {
return false;
}
if( (propertyName != null ? propertyName.equals(other.propertyName) : other.propertyName == null) &&
(typeName != null ? typeName.equals(other.typeName) : other.typeName == null) &&
(viewName != null ? viewName.equals(other.viewName) : other.viewName == null) &&
(elementType != null ? elementType.equals(other.elementType) : other.elementType == null)
)
{
return true;
}
return false;
}
@Override
public int hashCode() {
int h = 17;
h = 31 * h + alias.hashCode();
h = propertyName != null ? (31 * h + propertyName.hashCode()) : h;
h = typeName != null ? (31 * h + typeName.hashCode()) : h;
h = viewName != null ? (31 * h + viewName.hashCode()) : h;
h = elementType != null ? (31 * h + elementType.hashCode()) : h;
return h;
}
public Class getTypeJavaClass() {
return getJavaClass(this.typeName);
}
public Class getElementTypeJavaClass() {
return getJavaClass(this.elementType);
}
public Class getJavaClass(String tname) {
if(typeToClassMap.containsKey(tname.toUpperCase())) {
return typeToClassMap.get(tname.toUpperCase());
}
// Try to find the class using the class loader
try {
return Thread.currentThread().getContextClassLoader().loadClass(tname);
} catch (ClassNotFoundException e) {
throw ClassUtil.wrapRun(e);
}
}
private static Map typeToClassMap = new HashMap<>();
static {
typeToClassMap.put("LIST", List.class);
typeToClassMap.put("OBJECT", Object.class);
typeToClassMap.put("STRING", String.class);
typeToClassMap.put("BIGDECIMAL", BigDecimal.class);
typeToClassMap.put("BIGINTEGER", BigInteger.class);
typeToClassMap.put("BOOLEAN", Boolean.class);
typeToClassMap.put("INTEGER", Integer.class);
typeToClassMap.put("LONG", Long.class);
typeToClassMap.put("FLOAT", Float.class);
typeToClassMap.put("DOUBLE", Double.class);
typeToClassMap.put("BYTEARRAY", Byte[].class);
typeToClassMap.put("DATE", Date.class);
}
public boolean isSimple() {
// This is a simple type if it doesn't represent an TO_ONE or a TO_MANY relationship to an entity type
return !typeName.toUpperCase().equals("OBJECT") && (elementType == null || !elementType.toUpperCase().equals("OBJECT"));
}
}
public void initAliases() {
// check and initialize if needed
if(getFunction() != null) {
for (Function function : getFunction()) {
if (function.type == FunctionType.ALIAS) {
AliasHandler ah = (AliasHandler)function.getHandler();
PropertyAlias pa = new PropertyAlias(
function.getName(),
function.getAttribute(),
ah.getType(),
ah.getViewName(),
ah.getElementType());
if(pa.isViewReference()) {
viewAliasMap.put(function.getAttribute(), pa);
} else {
aliasMap.put(function.getAttribute(), pa);
}
}
}
}
}
/**
* Return the aliases of a property
* @param path the path for which to check for alias
* @return the alias if present, null otherwise
*/
public PropertyAlias getAlias(String path) {
if(aliasMap.size() == 0) {
// check and initialize if needed
initAliases();
}
// Get the anchor path for the view reference
path = extractAnchor(path);
return aliasMap.get(path);
}
public static String extractAnchor(String propertyPath) {
if(propertyPath.contains(VIEW_REFERENCE_START)) {
propertyPath = propertyPath.substring(0, propertyPath.indexOf(VIEW_REFERENCE_START));
}
return propertyPath;
}
public Set getAliases() {
return new HashSet(aliasMap.values());
}
public Set getViewAliases() {
return new HashSet(viewAliasMap.values());
}
@Override public boolean isSplitToRoot ()
{
return this.isSplitToRoot;
}
@Override public void setSplitToRoot (boolean value)
{
this.isSplitToRoot = value;
}
}