/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.mappingsmodel.query.relational; import java.util.List; import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel; import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWTableDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.query.MWQuery; import org.eclipse.persistence.tools.workbench.mappingsmodel.query.MWQueryParameter; import org.eclipse.persistence.tools.workbench.mappingsmodel.query.MWQueryable; import org.eclipse.persistence.tools.workbench.utility.node.Node; import org.eclipse.persistence.tools.workbench.utility.node.Problem; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.expressions.ExpressionBuilder; import org.eclipse.persistence.expressions.ExpressionOperator; import org.eclipse.persistence.internal.expressions.ConstantExpression; import org.eclipse.persistence.internal.expressions.FunctionExpression; import org.eclipse.persistence.internal.expressions.ParameterExpression; import org.eclipse.persistence.internal.expressions.QueryKeyExpression; import org.eclipse.persistence.internal.expressions.RelationExpression; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; /** * Abstract class extended by MWBinaryExpression and MWUnaryExpression * * This class holds on to the first argument. */ public final class MWBasicExpression extends MWExpression implements MWQueryableArgumentParent { private volatile MWQueryableArgument firstArgument; public final static String FIRST_ARUGMENT_PROPERTY = "firstArgument"; private volatile MWArgument secondArgument; //querykey, parameter, or literal public final static String SECOND_ARGUMENT_PROPERTY = "secondArgument"; //Operators public static final String EQUAL = "EQUAL"; public static final String EQUALS_IGNORE_CASE = "EQUALS IGNORE CASE"; public static final String GREATER_THAN = "GREATER THAN"; public static final String GREATER_THAN_EQUAL = "GREATER THAN EQUAL"; public static final String LESS_THAN = "LESS THAN"; public static final String LESS_THAN_EQUAL = "LESS THAN EQUAL"; public static final String LIKE = "LIKE"; public static final String LIKE_IGNORE_CASE = "LIKE IGNORE CASE"; public static final String NOT_EQUAL = "NOT EQUAL"; public static final String NOT_LIKE = "NOT LIKE"; public static final String IS_NULL = "IS NULL"; public static final String NOT_NULL = "NOT NULL"; /** * Default constructor - for TopLink use only. */ protected MWBasicExpression() { super(); } //parent will always be a BldrCompoundExpression MWBasicExpression(MWCompoundExpression parent, String operator) { super(parent, operator); } protected void addChildrenTo(List children) { super.addChildrenTo(children); children.add(this.firstArgument); children.add(this.secondArgument); } protected void initialize(Node parent) { super.initialize(parent); this.firstArgument = createDefaultQueryableArgument(); this.secondArgument = createDefaultLiteralArgument(); } private MWTableDescriptor getOwningDescriptor() { return (MWTableDescriptor) this.getParentQuery().getOwningDescriptor(); } MWQueryableArgument createDefaultQueryableArgument() { MWQueryable queryable = getOwningDescriptor().firstQueryable(); return new MWQueryableArgument(this, queryable); } void recalculateQueryables() { getFirstArgument().recalculateQueryables(); getSecondArgument().recalculateQueryables(); } public String displayString() { //should not use operatorType here for the display string, this is not resource bundled //maybe we should make an operator class just for the display string String displayString = getFirstArgument().displayString() +" "+ getOperatorType(); displayString += " " + getSecondArgument().displayString(); return displayString; } public MWQueryableArgument getFirstArgument() { return this.firstArgument; } public String getIndex() { return getParentCompoundExpression().getIndex() + Integer.toString(getParentCompoundExpression().getIndexOf(this)) +"."; } public MWQuery getParentQuery() { return getParentCompoundExpression().getParentQuery(); } public MWCompoundExpression getParentCompoundExpression() { return (MWCompoundExpression) getParent(); } public MWCompoundExpression getRootCompoundExpression() { return getParentCompoundExpression().getRootCompoundExpression(); } public MWArgument getSecondArgument() { return this.secondArgument; } private void setSecondArgument(MWArgument secondArgument) { if (operatorIsUnary(getOperatorType()) && !(secondArgument instanceof MWNullArgument)) { return; } MWArgument oldSecondArgument = getSecondArgument(); this.secondArgument = secondArgument; firePropertyChanged(SECOND_ARGUMENT_PROPERTY, oldSecondArgument, getSecondArgument()); getRootCompoundExpression().propertyChanged(this, SECOND_ARGUMENT_PROPERTY, oldSecondArgument, secondArgument); } public void setSecondArgumentToLiteral() { if (getSecondArgument().getType() != MWArgument.LITERAL_TYPE) { setSecondArgument(createDefaultLiteralArgument()); } } public void setSecondArgumentToParameter() { if (getSecondArgument().getType() != MWArgument.PARAMETER_TYPE) { setSecondArgument(createDefaultQueryParameterArgument()); } } public void setSecondArgumentToQueryable() { if (getSecondArgument().getType() != MWArgument.QUERY_KEY_TYPE) { setSecondArgument(createDefaultQueryableArgument()); } } private MWLiteralArgument createDefaultLiteralArgument() { return new MWLiteralArgument(this); } private MWQueryParameterArgument createDefaultQueryParameterArgument() { MWQueryParameter queryParameter = null; if (this.getParentQuery().parametersSize() > 0) queryParameter = this.getParentQuery().getParameter(0); return new MWQueryParameterArgument(this, queryParameter); } public void clearExpressions() { //do nothing because a BasicExpression has no sub expressions } public void undoChange(String propertyName, Object oldValue, Object newValue) { super.undoChange(propertyName, oldValue, newValue); if (propertyName == FIRST_ARUGMENT_PROPERTY) { setFirstArgument((MWQueryableArgument) oldValue); } if (propertyName == SECOND_ARGUMENT_PROPERTY) { setSecondArgument((MWArgument) oldValue); } } public void propertyChanged(Undoable container, String propertyName, Object oldValue, Object newValue) { getRootCompoundExpression().propertyChanged(container, propertyName, oldValue, newValue); } protected void setFirstArgument(MWQueryableArgument firstArgument) { MWArgument oldFirstArgument = getFirstArgument(); this.firstArgument = firstArgument; firePropertyChanged(FIRST_ARUGMENT_PROPERTY, oldFirstArgument, getFirstArgument()); getRootCompoundExpression().propertyChanged(this, FIRST_ARUGMENT_PROPERTY, oldFirstArgument, firstArgument); } private boolean operatorIsUnary(String operator) { return (operator == IS_NULL || operator == NOT_NULL); } //used to check if morphing between MWBinaryExpresison and MWUnaryExpression is needed private boolean operatorTypeHasChangedBetweenBinaryAndUnary(String oldOperatorType, String operatorType) { if (!operatorIsUnary(oldOperatorType)) { if (operatorIsUnary(operatorType)) return true; else return false; } else if (!operatorIsUnary(operatorType)) return true; return false; } public void setOperatorType(String operatorType) { String oldOperatorType = this.getOperatorType(); super.setOperatorType(operatorType); if (operatorTypeHasChangedBetweenBinaryAndUnary(oldOperatorType, operatorType)) { if (operatorIsUnary(operatorType)) { setSecondArgument(new MWNullArgument(this)); } else { setSecondArgumentToLiteral(); } } if (operatorIsStringType()) { getSecondArgument().operatorTypeChanged(); } } //If one of these operators is chosen and the user choose Literal //for the type of the second arument, then the literal must be of type String //The ui will disable all other options. public boolean operatorIsStringType() { return (getOperatorType() == EQUALS_IGNORE_CASE || getOperatorType() == LIKE || getOperatorType() == LIKE_IGNORE_CASE || getOperatorType() == NOT_LIKE); } // **************** problem support ***************** protected void addProblemsTo(List currentProblems) { super.addProblemsTo(currentProblems); this.checkReferenceMappingQueryableChosenWithoutUnaryOperatorChosen(currentProblems); } public void addQueryableNullProblemTo(List currentProblems) { currentProblems.add(queryableNullProblem()); } private Problem queryableNullProblem() { String queryName = getParentQuery().getName(); String lineNumber = getIndex(); return buildProblem(ProblemConstants.DESCRIPTOR_QUERY_EXPRESSION_NO_QUERY_KEY_SPECIFIED, lineNumber, queryName); } public Problem queryableInvalidProblem(MWQueryable queryable) { return buildProblem(ProblemConstants.DESCRIPTOR_QUERY_EXPRESSION_QUERY_KEY_NOT_VALID, queryable.displayString(), getParentQuery().signature()); } public boolean isQueryableValid(MWQueryable queryable) { return queryable.isValidForQueryExpression(); } private void checkReferenceMappingQueryableChosenWithoutUnaryOperatorChosen(List currentProblems) { if (checkIfNonLeafQueryableChosenWithoutOperatorIsNullOrNotNull() != null) { String queryName = getParentQuery().getName(); String lineNumber = checkIfNonLeafQueryableChosenWithoutOperatorIsNullOrNotNull().getIndex(); currentProblems.add(buildProblem(ProblemConstants.DESCRIPTOR_QUERY_EXPRESSION_NON_UNARY_OPERATOR, lineNumber, queryName)); } } private MWBasicExpression checkIfNonLeafQueryableChosenWithoutOperatorIsNullOrNotNull() { MWQueryableArgument argument = getFirstArgument(); if (argument.getQueryableArgumentElement().getQueryable() != null && argument.getQueryableArgumentElement().getQueryable().allowsChildren()) { if (this.getOperatorType() != IS_NULL && this.getOperatorType() != NOT_NULL) { return this; } } return null; } public void toString(StringBuffer sb) { super.toString(sb); sb.append("firstArgument = " ); sb.append(getFirstArgument()); sb.append(", operator = " ); sb.append(getOperatorType()); } //Persistence public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWBasicExpression.class); //Inheritance Policy descriptor.getInheritancePolicy().setParentClass(MWExpression.class); //Mappings // 1-1 to the first BldrArgument XMLCompositeObjectMapping firstArgumentMapping = new XMLCompositeObjectMapping(); firstArgumentMapping.setAttributeName("firstArgument"); firstArgumentMapping.setReferenceClass(MWQueryableArgument.class); firstArgumentMapping.setXPath("first-argument"); descriptor.addMapping(firstArgumentMapping); XMLCompositeObjectMapping secondArgumentMapping = new XMLCompositeObjectMapping(); secondArgumentMapping.setAttributeName("secondArgument"); secondArgumentMapping.setReferenceClass(MWArgument.class); secondArgumentMapping.setXPath("second-argument"); descriptor.addMapping(secondArgumentMapping); return descriptor; } // ***************** Runtime Conversion *********** Expression buildRuntimeExpression(ExpressionBuilder builder) { Expression firstExpression = getFirstArgument().runtimeExpression(builder); Expression secondExpression = getSecondArgument().runtimeExpression(builder); //TODO make operatorType an Object instead of a String? if (getOperatorType() == EQUAL) { return firstExpression.equal(secondExpression); } else if (getOperatorType() == GREATER_THAN) { return firstExpression.greaterThan(secondExpression); } else if (getOperatorType() == GREATER_THAN_EQUAL) { return firstExpression.greaterThanEqual(secondExpression); } else if (getOperatorType() == LESS_THAN) { return firstExpression.lessThan(secondExpression); } else if (getOperatorType() == LESS_THAN_EQUAL) { return firstExpression.lessThanEqual(secondExpression); } else if (getOperatorType() == NOT_EQUAL) { return firstExpression.notEqual(secondExpression); } else if (getOperatorType() == EQUALS_IGNORE_CASE) { return firstExpression.equalsIgnoreCase(secondExpression); } else if (getOperatorType() == LIKE) { return firstExpression.like(secondExpression); } else if (getOperatorType() == LIKE_IGNORE_CASE) { return firstExpression.likeIgnoreCase(secondExpression); } else if (getOperatorType() == NOT_LIKE) { return firstExpression.notLike(secondExpression); } else if (getOperatorType() == IS_NULL) { return firstExpression.isNull(); } else if (getOperatorType() == NOT_NULL) { return firstExpression.notNull(); } else { throw new IllegalStateException("Operator type: " + getOperatorType() + " is not supported"); } } static MWBasicExpression convertFromRuntime(MWCompoundExpression parent, Expression expression) { ExpressionOperator runtimeOperator = expression.getOperator(); String bldrOperator = EQUAL; if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.Equal))) { bldrOperator = EQUAL; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.NotEqual))) { bldrOperator = NOT_EQUAL; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.LessThan))) { bldrOperator = LESS_THAN; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.LessThanEqual))) { bldrOperator = LESS_THAN_EQUAL; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.GreaterThan))) { bldrOperator = GREATER_THAN; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.GreaterThanEqual))) { bldrOperator = GREATER_THAN_EQUAL; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.Like))) { bldrOperator = LIKE; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.NotLike))) { bldrOperator = NOT_LIKE; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.IsNull))) { bldrOperator = IS_NULL; } else if (runtimeOperator == ExpressionOperator.getOperator(new Integer(ExpressionOperator.NotNull))) { bldrOperator = NOT_NULL; } MWBasicExpression bldrExpression = new MWBasicExpression(parent, bldrOperator); if (bldrOperator == MWBasicExpression.NOT_NULL || bldrOperator == MWBasicExpression.IS_NULL) { Expression firstChildExpression = ((FunctionExpression)expression).getBaseExpression(); bldrExpression.setFirstArgument(MWQueryableArgument.convertFromRuntime(bldrExpression, (QueryKeyExpression) firstChildExpression)); } else { Expression firstChildExpression = ((RelationExpression) expression).getFirstChild(); //this will only happen if the user choose LikeIgnoreCase or EqualsIgnoreCase if (firstChildExpression.isFunctionExpression()) { firstChildExpression = ((FunctionExpression) firstChildExpression).getBaseExpression(); if (bldrExpression.getOperatorType() == MWBasicExpression.EQUAL) { bldrExpression.setOperatorType(MWBasicExpression.EQUALS_IGNORE_CASE); } else { bldrExpression.setOperatorType(MWBasicExpression.LIKE_IGNORE_CASE); } } bldrExpression.setFirstArgument(MWQueryableArgument.convertFromRuntime(bldrExpression, (QueryKeyExpression) firstChildExpression)); Expression secondChildExpression =((RelationExpression) expression).getSecondChild(); //this will only happen if the user choose LikeIgnoreCase or EqualsIgnoreCase if (secondChildExpression.isFunctionExpression()) { secondChildExpression = ((FunctionExpression) secondChildExpression).getBaseExpression(); } //convert the second child which can be a queryableArgument, parameterArgument, or literaArgument if (secondChildExpression.isQueryKeyExpression()) { bldrExpression.setSecondArgument(MWQueryableArgument.convertFromRuntime(bldrExpression, (QueryKeyExpression) secondChildExpression)); } else if (secondChildExpression.isConstantExpression()) { bldrExpression.setSecondArgument(MWLiteralArgument.convertFromRuntime(bldrExpression, (ConstantExpression) secondChildExpression)); } else if (secondChildExpression.isParameterExpression()) { bldrExpression.setSecondArgument(MWQueryParameterArgument.convertFromRuntime(bldrExpression, (ParameterExpression) secondChildExpression)); } } return bldrExpression; } }