/* * 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.apache.geode.cache.query.internal; import org.apache.geode.internal.i18n.LocalizedStrings; import java.util.*; import java.lang.reflect.Method; import org.apache.geode.cache.query.*; import org.apache.geode.cache.query.internal.index.IndexCreationHelper; import org.apache.geode.cache.query.internal.types.TypeUtils; import org.apache.geode.cache.query.types.*; /** * Value representing a current iteration element. This is the representation used during * evaluation. * * A RuntimeIterator can be in one of two states. If it is independent of the current context scope * then its collection is evaluated lazily, in which case collection is initialized and knows its * elementType. The elementType field is the same value as in the collection. Otherwise, collection * is UNINITIALIZED and the elementType is set in any case. * * A RuntimeIterator can also be named or anonymous (name is null). * */ public class RuntimeIterator extends AbstractCompiledValue { // token to differentiate null from uninitialized private static final SelectResults UNINITIALIZED = new ResultsBag(0, null); private Object current = UNINITIALIZED; private String name; private SelectResults collection = UNINITIALIZED; private CompiledIteratorDef cmpIteratorDefn; private ObjectType elementType; // may be more specific than that in // cmpIteratorDefn // for canonicalization private String internalId = null; private String definition = null; private String index_internal_id = null; private int scopeID = -1; public int getType() { return ITERATOR_DEF; } public ObjectType getElementType() { return this.elementType; } RuntimeIterator(CompiledIteratorDef cmpIteratorDefn, ObjectType elementType) { if (elementType == null || cmpIteratorDefn == null) { throw new IllegalArgumentException( LocalizedStrings.RuntimeIterator_ELEMENTTYPE_ANDOR_CMPITERATORDEFN_SHOULD_NOT_BE_NULL .toLocalizedString()); } this.name = cmpIteratorDefn.getName(); this.elementType = elementType; this.cmpIteratorDefn = cmpIteratorDefn; } // public RuntimeIterator(String name, SelectResults collection) { // if (collection == null) // throw new IllegalArgumentException("base collection must not be null"); // // this.name = name; // may be null // this.collection = collection; // this.cmpIteratorDefn = null; // this.elementType = collection.getCollectionType().getElementType(); // } // /** Return true if this is an iterator that is dependent on other // iterator(s) // * in this scope (a cached result from isDependentOn(context)) // */ // public boolean isDependent() { // return this.isDependent; // } CompiledIteratorDef getCmpIteratorDefn() { return this.cmpIteratorDefn; } public String getName() { return this.name; } /** * (Re)evaluate in the context of the current iterations through the cross-product. If this * iterator is not dependent on the current iteration, then just return the previously evaluated * collection. Otherwise, re-evaluate. Returns null if the collection itself is null or UNDEFINED */ public SelectResults evaluateCollection(ExecutionContext context) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { if (this.collection != UNINITIALIZED && !this.cmpIteratorDefn.isDependentOnAnyIteratorOfScopeLessThanItsOwn(context) && this.scopeID != IndexCreationHelper.INDEX_QUERY_SCOPE_ID) { return this.collection; } // limit the scope for evaluation to this RuntimeIterator: // we don't want to use this iterator or subsequent ones in the from clause // to evaluate this collection. this.collection = this.cmpIteratorDefn.evaluateCollection(context, this); if (this.collection == null) { return null; } // if we already have a more specific elementType, set it in the collection if (!this.elementType.equals(TypeUtils.OBJECT_TYPE)) { this.collection.setElementType(elementType); } else { // Asif : The elementType in the Collection obtained is more // specific . So use that type. this.elementType = collection.getCollectionType().getElementType(); } return this.collection; } @Override public Set computeDependencies(ExecutionContext context) { // called as the receiver of Path or Operation return Collections.singleton(this); } @Override public boolean isDependentOnIterator(RuntimeIterator itr, ExecutionContext context) { if (itr == this) return true; // never true(?) return this.cmpIteratorDefn.isDependentOnIterator(itr, context); } @Override public boolean isDependentOnCurrentScope(ExecutionContext context) { return this.cmpIteratorDefn.isDependentOnCurrentScope(context); } public void setCurrent(Object current) { this.current = current; } public Object evaluate(ExecutionContext context) { Support.Assert(current != UNINITIALIZED, "error to evaluate RuntimeIterator without setting current first"); return this.current; } boolean containsProperty(String name, int numArgs, boolean mustBeMethod) throws AmbiguousNameException { // first handle structs if ((this.elementType instanceof StructType) && !mustBeMethod) { // check field names String fieldName[] = ((StructType) this.elementType).getFieldNames(); for (int i = 0; i < fieldName.length; i++) { if (name.equals(fieldName[i])) { return true; } } } Class clazz = this.elementType.resolveClass(); if (numArgs > 0 || mustBeMethod) { // if numArgs==0, then just look up method directly // instead of sifting through all methods if (numArgs == 0) { try { clazz.getMethod(name, (Class[]) null); return true; } catch (NoSuchMethodException e) { return false; } } // enumerate methods and match with name // here, only check for a method with the same number of arguments. // we'll check for ambiguous method invocation when the method is // actually fully resolved and invoked Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { Method m = methods[i]; if (m.getName().equals(name) && m.getParameterTypes().length == numArgs) return true; } return false; } // if there are zero arguments and it's an attribute, then defer to // AttributeDescriptor // to see if there's a match return new AttributeDescriptor(name).validateReadType(clazz); } // private SelectResults prepareIteratorDef(Object obj) // throws TypeMismatchException { // if (obj == null) { // return null; // } // // if (obj == QueryService.UNDEFINED) { // return null; // } // // if (obj instanceof SelectResults) { // // probably came from nested query or is a QRegion already from region // path // return (SelectResults)obj; // } // // if (obj instanceof Region) { // return new QRegion((Region)obj); // this can happen if region passed in as // parameter // } // // // if this is a domain collection, it should be unmodifiable // // if obj is a Collection but not a SelectResults, it must be from the // // domain, otherwise it would be a SelectResults. // if (obj instanceof Collection) { // // do not lose ordering and duplicate information, // ResultsCollectionWrapper res = // new ResultsCollectionWrapper(this.elementType, (Collection)obj); // res.setModifiable(false); // return res; // } // // // Object[] is wrapped and considered a domain object so unmodifiable // if (obj instanceof Object[]) { // // the element type is specified in the array itself, unless we have // // something more specific // if (this.elementType.equals(TypeUtils.OBJECT_TYPE)) { // if we don't have // constraint info // this.elementType = // TypeUtils.getObjectType(obj.getClass().getComponentType()); // } // // // do not lose ordering and duplicate information, // ResultsCollectionWrapper res = // new ResultsCollectionWrapper(this.elementType, // Arrays.asList((Object[])obj)); // res.setModifiable(false); // return res; // } // // if (obj instanceof Map) { // if (this.elementType.equals(TypeUtils.OBJECT_TYPE)) { // if we don't have // more specific type info, use Map.Entry // elementType = TypeUtils.getObjectType(Map.Entry.class); // } // ResultsCollectionWrapper res = // new ResultsCollectionWrapper(elementType, ((Map)obj).entrySet()); // res.setModifiable(false); // return res; // } else { // throw new TypeMismatchException( // "The expression in the FROM clause of a SELECT statement was type '" // + obj.getClass().getName() // + "', which cannot be interpreted as a collection"); // } // } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(this.getClass().getName()); sb.append(" (name=" + this.name); // if(isDependent) sb.append(" collection expr=" + cmpIteratorDefn); // else { // sb.append("; collection=" +this.collection + ")"); // sb.append("; collectionType=" +this.collection.getCollectionType() + // ")"); // sb.append("; elementType=" // +this.collection.getCollectionType().getElementType() + ")"); // } return sb.toString(); } // Canonicalization public void setInternalId(String id) { // it's okay for this to be set more than once; a RuntimeIterator // can be bound to a scope to compute dependencies, then // re-bound to a different scope later at eval time. // Support.Assert((internalId == null), "Internal ID is already set"); Support.Assert((id != null), "Internal ID can not be null"); internalId = id; } public String getInternalId() { // Support.Assert((internalId != null), "Internal ID is not yet set"); return internalId; } public void setDefinition(String def) { Support.Assert((definition == null), "Definition is already set"); Support.Assert((def != null), "Definition can not be null"); definition = def; } public void setIndexInternalID(String index_id) { this.index_internal_id = index_id; } public String getIndexInternalID() { return this.index_internal_id; } public String getDefinition() { Support.Assert((definition != null), "Definition is not yet set"); return definition; } @Override public void generateCanonicalizedExpression(StringBuffer clauseBuffer, ExecutionContext context) throws AmbiguousNameException, TypeMismatchException { // Asif: prepend the internal iterator variable name for this // RunTimeIterator // int currScopeID = context.currentScope().getScopeID(); if (currScopeID == this.scopeID) { // Support.Assert(this.index_internal_id != null, "Index_Internal_ID // should have been set at this point"); clauseBuffer.insert(0, this.index_internal_id == null ? this.internalId : this.index_internal_id); } else { clauseBuffer.insert(0, internalId).insert(0, '_').insert(0, this.scopeID).insert(0, "scope"); } } void setScopeID(int scopeID) { this.scopeID = scopeID; } int getScopeID() { return this.scopeID; } }