blob: ea0009c6cb08b49f6c9672823019ccc313feb5dc [file] [log] [blame]
/*
* 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.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.geode.cache.query.AmbiguousNameException;
import org.apache.geode.cache.query.NameNotFoundException;
import org.apache.geode.cache.query.NameResolutionException;
import org.apache.geode.cache.query.QueryInvocationTargetException;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.Struct;
import org.apache.geode.cache.query.TypeMismatchException;
import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes;
import org.apache.geode.cache.query.internal.types.TypeUtils;
import org.apache.geode.cache.query.types.ObjectType;
/**
* Class Description
*
* @version $Revision: 1.1 $
*/
public class PathUtils {
public static String[] tokenizePath(String path) {
ArrayList alist = new ArrayList();
StringTokenizer tokenizer = new StringTokenizer(path, ".");
while (tokenizer.hasMoreTokens()) {
alist.add(tokenizer.nextToken());
}
return (String[]) alist.toArray(new String[0]);
}
public static String buildPathString(String[] path) {
Support.assertArg(path != null && path.length > 0, "path should not be null or empty");
StringBuffer buf = new StringBuffer();
buf.append(path[0]);
for (int i = 1; i < path.length; i++) {
buf.append('.');
buf.append(path[i]);
}
return buf.toString();
}
public static Object evaluateAttribute(ExecutionContext context, Object target, String attribute)
throws NameNotFoundException, QueryInvocationTargetException {
if (target instanceof Struct) {
Struct struct = (Struct) target;
try {
return struct.get(attribute);
} catch (Exception e) {
throw new NameNotFoundException(attribute);
}
}
try {
return new AttributeDescriptor(context.getCache().getPdxRegistry(), attribute)
.read(target, context);
} catch (NameNotFoundException nfe) {
if (DefaultQueryService.QUERY_HETEROGENEOUS_OBJECTS
|| DefaultQueryService.TEST_QUERY_HETEROGENEOUS_OBJECTS) {
return QueryService.UNDEFINED;
} else {
throw nfe;
}
}
}
/*
* This was added as part of CQ performance changes. The change is done to re-use the
* AttributeDescriptor object instead of creating it each time.
*/
/*
* public static Object evaluateAttribute(Object target, String attribute, AttributeDescriptor
* attributeDescriptor) throws NameNotFoundException, QueryInvocationTargetException { if(target
* instanceof Struct){ Struct struct = (Struct)target; try{ return struct.get(attribute);
* }catch(Exception e){ throw new NameNotFoundException(attribute); } } return
* attributeDescriptor.read(target); }
*/
/**
* @param pathArray the path starting with an attribute on the initial type.
* @return array of types starting with the initialType and ending with the type of the last
* attribute in the path.
* @throws NameNotFoundException if could not find an attribute along path
*
*/
public static ObjectType[] calculateTypesAlongPath(ExecutionContext context,
ObjectType initialType, String[] pathArray) throws NameNotFoundException {
ObjectType[] types = new ObjectType[pathArray.length + 1];
// initialClass goes in front
types[0] = initialType;
for (int i = 1; i < types.length; i++) {
ObjectType currentType = types[i - 1];
Member member = new AttributeDescriptor(context.getCache().getPdxRegistry(), pathArray[i - 1])
.getReadMember(currentType.resolveClass());
if (member instanceof Field)
types[i] = TypeUtils.getObjectType(((Field) member).getType());
else if (member instanceof Method)
types[i] = TypeUtils.getObjectType(((Method) member).getReturnType());
}
return types;
}
public static ObjectType computeElementTypeOfExpression(ExecutionContext context,
CompiledValue expr) throws AmbiguousNameException {
try {
ObjectType type = TypeUtils.OBJECT_TYPE;
List exprSteps = new ArrayList();
while (true) {
if (expr instanceof CompiledPath) {
CompiledPath path = (CompiledPath) expr;
exprSteps.add(0, path.getTailID());
expr = path.getReceiver();
} else if (expr instanceof CompiledOperation) {
CompiledOperation operation = (CompiledOperation) expr;
if (operation.getArguments().size() > 0) {
return TypeUtils.OBJECT_TYPE;
}
exprSteps.add(0, operation.getMethodName() + "()");
expr = operation.getReceiver(context);
if (expr == null) {
expr = context.resolveImplicitOperationName(operation.getMethodName(),
operation.getArguments().size(), true);
}
} else if (expr instanceof CompiledID) {
expr = context.resolve(((CompiledID) expr).getId());
} else if (expr instanceof CompiledRegion) {
QRegion qrgn = (QRegion) ((CompiledRegion) expr).evaluate(context);
type = qrgn.getCollectionType();
break;
} else if (expr instanceof RuntimeIterator) {
type = ((RuntimeIterator) expr).getElementType();
break;
} else {
return TypeUtils.OBJECT_TYPE;
}
}
if (!TypeUtils.OBJECT_TYPE.equals(type)) {
Class clazz = type.resolveClass();
for (int i = 0; i < exprSteps.size(); i++) {
Member member;
String stepStr = (String) exprSteps.get(i);
// System.out.println("step = "+step);
if (stepStr.endsWith("()")) {
stepStr = stepStr.substring(0, stepStr.length() - 2);
member = clazz.getMethod(stepStr, (Class[]) null);
} else {
member = new AttributeDescriptor(context.getCache().getPdxRegistry(), stepStr)
.getReadMember(clazz);
}
if (member instanceof Field) {
clazz = ((Field) member).getType();
} else if (member instanceof Method) {
clazz = ((Method) member).getReturnType();
}
type = TypeUtils.getObjectType(clazz);
}
return type;
}
} catch (NoSuchMethodException e) {
} catch (NameResolutionException e) {
} catch (TypeMismatchException e) {
}
return TypeUtils.OBJECT_TYPE;
}
/**
* Collects all the compiled values in the path , starting from the self at position 0 in the
* returned List
*
* @return List of CompiledValues ( includes the RuntimeIterator)
*/
public static List<CompiledValue> collectCompiledValuesInThePath(CompiledValue expr,
ExecutionContext context) throws AmbiguousNameException, TypeMismatchException {
boolean toContinue = true;
List<CompiledValue> retList = new ArrayList<CompiledValue>();
int exprType = expr.getType();
while (toContinue) {
switch (exprType) {
case OQLLexerTokenTypes.RegionPath:
retList.add(expr);
toContinue = false;
break;
case OQLLexerTokenTypes.METHOD_INV:
retList.add(expr);
CompiledOperation operation = (CompiledOperation) expr;
expr = operation.getReceiver(null/*
* pass the ExecutionContext as null, thus never
* implicitly resolving to RuntimeIterator
*/);
if (expr == null) {
expr = operation;
toContinue = false;
}
break;
case CompiledValue.PATH:
retList.add(expr);
expr = ((CompiledPath) expr).getReceiver();
break;
case OQLLexerTokenTypes.ITERATOR_DEF:
retList.add(expr);
toContinue = false;
break;
case OQLLexerTokenTypes.TOK_LBRACK:
retList.add(expr);
expr = ((CompiledIndexOperation) expr).getReceiver();
break;
case OQLLexerTokenTypes.Identifier:
CompiledID cid = (CompiledID) expr;
expr = context.resolve(cid.getId());
break;
default:
toContinue = false;
break;
}
if (toContinue)
exprType = expr.getType();
}
return retList;
}
}