/******************************************************************************* * 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.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel; import org.eclipse.persistence.tools.workbench.mappingsmodel.MWQueryKey; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWTableDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.query.MWQueryable; import org.eclipse.persistence.tools.workbench.utility.filters.Filter; import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator; import org.eclipse.persistence.tools.workbench.utility.node.Node; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.expressions.ExpressionBuilder; import org.eclipse.persistence.internal.expressions.QueryKeyExpression; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; /** * An MWQueryableArgument is always used as the left hand side of an MWBasicExpression. * It can also be used as the right hand side of an MWBinaryExpression. * * When the user chooses a new queryable for this argument, sets something to allowsNull, or makes any other change * to the queryable, a new QueryableArgumentElement is created */ public final class MWQueryableArgument extends MWArgument { private volatile MWQueryableArgumentElement queryableArgumentElement; //property change public final static String QUERYABLE_ARGUMENT_ELEMENT_PROPERTY = "queryableArgumentElement"; /** * Default constructor - for TopLink use only. */ private MWQueryableArgument() { super(); } MWQueryableArgument(MWQueryableArgumentParent parent) { super(parent); } MWQueryableArgument(MWQueryableArgumentParent parent, MWQueryable queryable) { this(parent); setQueryableArgument(queryable); } MWQueryableArgument(MWQueryableArgumentParent parent, Iterator queryables) { this(parent); setQueryableArgument(queryables); } MWQueryableArgument(MWQueryableArgumentParent parent, Iterator queryables, Iterator allowsNull) { this(parent); setQueryableArgument(queryables, allowsNull); } protected void addChildrenTo(List children) { super.addChildrenTo(children); children.add(this.queryableArgumentElement); } protected void initialize(Node parent) { super.initialize(parent); this.queryableArgumentElement = new MWQueryableArgumentElement(this); } public String getType() { return QUERY_KEY_TYPE; } public String displayString() { return getQueryableArgumentElement().displayString(); } //*********** problem support **************** protected void addProblemsTo(List currentProblems) { super.addProblemsTo(currentProblems); this.checkQueryable(currentProblems); } private void checkQueryable(List currentProblems) { MWQueryable queryable = this.getQueryableArgumentElement().getQueryable(); if (queryable == null) { this.getQueryableArgumentParent().addQueryableNullProblemTo(currentProblems); } else { if ( ! this.getQueryableArgumentParent().isQueryableValid(queryable)) { currentProblems.add(this.getQueryableArgumentParent().queryableInvalidProblem(queryable)); } } } // ********** model synchronization support ********** public void nodeRemoved(Node node) { super.nodeRemoved(node); if (node instanceof MWMapping) { mappingReplaced((MWMapping) node, null); } if (node instanceof MWQueryKey) { //checking to see if any of the joined queryable elements have a null //queryable object at this point. MWQueryableArgumentElement queryableArgumentElement = getQueryableArgumentElement().getJoinedQueryableElement(); while(queryableArgumentElement != null) { if (queryableArgumentElement.getQueryable() == null || queryableArgumentElement.getQueryable().isLeaf(Filter.NULL_INSTANCE)) { getQueryableArgumentElement().setQueryableToNull(); break; } queryableArgumentElement = queryableArgumentElement.getJoinedQueryableElement(); } } } public void descriptorUnmapped(Collection mappings) { super.descriptorUnmapped(mappings); for (Iterator stream = mappings.iterator(); stream.hasNext();) { mappingReplaced((MWMapping) stream.next(), null); } } public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) { super.mappingReplaced(oldMapping, newMapping); //checking to see if any of the joined queryable elements have a null //queryable object at this point. MWQueryableArgumentElement queryableArgumentElement = getQueryableArgumentElement().getJoinedQueryableElement(); while(queryableArgumentElement != null) { if (queryableArgumentElement.getQueryable() == null || queryableArgumentElement.getQueryable().isLeaf(Filter.NULL_INSTANCE)) { getQueryableArgumentElement().setQueryableToNull(); break; } queryableArgumentElement = queryableArgumentElement.getJoinedQueryableElement(); } } public void undoChange(String propertyName, Object oldValue, Object newValue) { if (propertyName == QUERYABLE_ARGUMENT_ELEMENT_PROPERTY) setQueryableArgumentElement((MWQueryableArgumentElement) oldValue); } public MWQueryableArgumentElement getQueryableArgumentElement() { return this.queryableArgumentElement; } /** * Used for setting up a queryable argument which does not use joining */ public void setQueryableArgument(MWQueryable queryable) { List queryables = new ArrayList(); queryables.add(queryable); this.setQueryableArgument(queryables.iterator()); } /** * Used for setting up a queryable argument which uses joining. * * If the user chose manager(Allows Null).phoneNumbers.areaCode as the queryable argument, then * * queryables = {areaCode,phoneNumbers, manager} * allowsNull = {false, false, true} * * The iterator MUST always have 1 element. If it has more than 1 element, those * are the joined queryable elements. */ public void setQueryableArgument(Iterator queryables, Iterator allowsNull) { if (!queryables.hasNext()) { throw new UnsupportedOperationException(); } MWQueryable queryable = (MWQueryable) queryables.next(); queryables.remove(); MWQueryableArgumentElement queryableArgumentElement = new MWQueryableArgumentElement(this, queryable); if (allowsNull.hasNext()) { queryableArgumentElement.setAllowsNull(((Boolean) allowsNull.next()).booleanValue()); allowsNull.remove(); } if (queryables.hasNext()) { queryableArgumentElement.setJoinedQueryable(queryables, allowsNull); } setQueryableArgumentElement(queryableArgumentElement); } public void setQueryableArgument(Iterator queryables) { setQueryableArgument(queryables, NullIterator.instance()); } private void setQueryableArgumentElement(MWQueryableArgumentElement element) { MWQueryableArgumentElement oldElement = getQueryableArgumentElement(); this.queryableArgumentElement = element; firePropertyChanged(QUERYABLE_ARGUMENT_ELEMENT_PROPERTY, oldElement, element); getQueryableArgumentParent().propertyChanged(this, QUERYABLE_ARGUMENT_ELEMENT_PROPERTY, oldElement, element); } void recalculateQueryables() { MWQueryableArgumentElement element = getQueryableArgumentElement(); while (element.getJoinedQueryableElement() != null) { if (!element.getJoinedQueryableElement().getQueryable().subQueryableElements(Filter.NULL_INSTANCE).contains(element.getQueryable())) { getQueryableArgumentElement().setQueryableToNull(); break; } element = element.getJoinedQueryableElement(); } if (!((MWRelationalDescriptor) getQueryableArgumentParent().getParentQuery().getOwningDescriptor()).getQueryables(Filter.NULL_INSTANCE).contains(element.getQueryable())) getQueryableArgumentElement().setQueryableToNull(); } public void toString(StringBuffer sb) { super.toString(sb); sb.append(getQueryableArgumentElement()); } //Persistence public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWQueryableArgument.class); descriptor.getInheritancePolicy().setParentClass(MWArgument.class); XMLCompositeObjectMapping queryableArgumentElementMapping = new XMLCompositeObjectMapping(); queryableArgumentElementMapping.setAttributeName("queryableArgumentElement"); queryableArgumentElementMapping.setReferenceClass(MWQueryableArgumentElement.class); queryableArgumentElementMapping.setXPath("queryable-argument-element"); descriptor.addMapping(queryableArgumentElementMapping); return descriptor; } //Conversion to Runtime Expression runtimeExpression(ExpressionBuilder builder) { return queryableArgumentElement.convertToRuntime(builder); } static MWQueryableArgument convertFromRuntime(MWBasicExpression bldrExpression, QueryKeyExpression runtimeExpression) { MWQueryableArgument newArgument = new MWQueryableArgument(bldrExpression); List queryableNames = new ArrayList(); List allowsNullList = new ArrayList(); queryableNames.add(0, runtimeExpression.getName()); allowsNullList.add(0, Boolean.valueOf(runtimeExpression.shouldUseOuterJoin())); while (runtimeExpression.getBaseExpression().isQueryKeyExpression()) { runtimeExpression = (QueryKeyExpression) runtimeExpression.getBaseExpression(); queryableNames.add(0, runtimeExpression.getName()); allowsNullList.add(Boolean.valueOf(runtimeExpression.shouldUseOuterJoin())); } String currentQueryableName = (String) queryableNames.remove(0); MWQueryable currentQueryable = ((MWTableDescriptor)newArgument.getQueryableArgumentParent().getParentQuery().getOwningDescriptor()).queryableNamed(currentQueryableName); List queryables = new ArrayList(); queryables.add(0, currentQueryable); while (queryableNames.size() > 0) { Iterator subQueryableElements = currentQueryable.subQueryableElements(Filter.NULL_INSTANCE).iterator(); String queryableName = (String) queryableNames.remove(0); while(subQueryableElements.hasNext()) { MWQueryable queryable = (MWQueryable) subQueryableElements.next(); if (queryable.getName().equals(queryableName)) { currentQueryable = queryable; break; } } queryables.add(0, currentQueryable); } newArgument.setQueryableArgument(queryables.iterator(), allowsNullList.iterator()); return newArgument; } }