| /* |
| * 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 java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.Set; |
| |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.cache.query.FunctionDomainException; |
| import org.apache.geode.cache.query.NameResolutionException; |
| import org.apache.geode.cache.query.QueryInvocationTargetException; |
| import org.apache.geode.cache.query.SelectResults; |
| import org.apache.geode.cache.query.TypeMismatchException; |
| 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.ObjectType; |
| import org.apache.geode.cache.query.types.StructType; |
| |
| /** |
| * 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 |
| @Immutable |
| private static final SelectResults UNINITIALIZED = new ResultsBag(0, null); |
| private Object current = UNINITIALIZED; |
| private final String name; |
| private SelectResults collection = UNINITIALIZED; |
| private final CompiledIteratorDef cmpIteratorDefn; |
| /** may be more specific than that in cmpIteratorDefn */ |
| private ObjectType elementType; |
| /** for canonicalization */ |
| private String internalId = null; |
| private String definition = null; |
| private String index_internal_id = null; |
| private int scopeID = -1; |
| |
| @Override |
| public int getType() { |
| return ITERATOR_DEF; |
| } |
| |
| public ObjectType getElementType() { |
| return elementType; |
| } |
| |
| RuntimeIterator(CompiledIteratorDef cmpIteratorDefn, ObjectType elementType) { |
| if (elementType == null || cmpIteratorDefn == null) { |
| throw new IllegalArgumentException( |
| "elementType and/or cmpIteratorDefn should not be null"); |
| } |
| this.name = cmpIteratorDefn.getName(); |
| this.elementType = elementType; |
| this.cmpIteratorDefn = cmpIteratorDefn; |
| } |
| |
| CompiledIteratorDef getCmpIteratorDefn() { |
| return cmpIteratorDefn; |
| } |
| |
| public String getName() { |
| return 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 (collection != UNINITIALIZED |
| && !cmpIteratorDefn.isDependentOnAnyIteratorOfScopeLessThanItsOwn(context) |
| && scopeID != IndexCreationHelper.INDEX_QUERY_SCOPE_ID) { |
| return 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. |
| collection = cmpIteratorDefn.evaluateCollection(context, this); |
| if (collection == null) { |
| return null; |
| } |
| // if we already have a more specific elementType, set it in the collection |
| if (!elementType.equals(TypeUtils.OBJECT_TYPE)) { |
| collection.setElementType(elementType); |
| } else { |
| // The elementType in the Collection obtained is more specific . So use that type. |
| elementType = collection.getCollectionType().getElementType(); |
| } |
| return 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 cmpIteratorDefn.isDependentOnIterator(itr, context); |
| } |
| |
| @Override |
| public boolean isDependentOnCurrentScope(ExecutionContext context) { |
| return cmpIteratorDefn.isDependentOnCurrentScope(context); |
| } |
| |
| public void setCurrent(Object current) { |
| this.current = current; |
| } |
| |
| @Override |
| public Object evaluate(ExecutionContext context) { |
| Support.Assert(current != UNINITIALIZED, |
| "error to evaluate RuntimeIterator without setting current first"); |
| return current; |
| } |
| |
| boolean containsProperty(ExecutionContext context, String name, int numArgs, |
| boolean mustBeMethod) { |
| // first handle structs |
| if ((elementType instanceof StructType) && !mustBeMethod) { |
| // check field names |
| String[] fieldName = ((StructType) elementType).getFieldNames(); |
| for (String s : fieldName) { |
| if (name.equals(s)) { |
| return true; |
| } |
| } |
| } |
| Class<?> clazz = 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 (Method m : methods) { |
| 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(context.getCache().getPdxRegistry(), name) |
| .validateReadType(clazz); |
| } |
| |
| @Override |
| public String toString() { |
| return getClass().getName() + " (name=" + name + " collection expr=" + cmpIteratorDefn + ")"; |
| } |
| |
| // Canonicalization |
| 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 index_internal_id; |
| } |
| |
| public String getDefinition() { |
| Support.Assert((definition != null), "Definition is not yet set"); |
| return definition; |
| } |
| |
| @Override |
| public void generateCanonicalizedExpression(StringBuilder clauseBuffer, |
| ExecutionContext context) { |
| // prepend the internal iterator variable name for this RunTimeIterator |
| int currScopeID = context.currentScope().getScopeID(); |
| if (currScopeID == scopeID) { |
| // should have been set at this point |
| clauseBuffer.insert(0, index_internal_id == null ? internalId : index_internal_id); |
| } else { |
| clauseBuffer.insert(0, internalId).insert(0, '_').insert(0, scopeID).insert(0, "scope"); |
| } |
| } |
| |
| void setScopeID(int scopeID) { |
| this.scopeID = scopeID; |
| } |
| |
| int getScopeID() { |
| return scopeID; |
| } |
| } |