/* * 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 com.aliyun.odps.udf.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.aliyun.odps.utils.StringUtils; import com.aliyun.odps.udf.OdpsType; import com.aliyun.odps.udf.UDFException; import com.aliyun.odps.udf.annotation.PreferWritable; import com.aliyun.odps.udf.annotation.Resolve; /** * UDF SDK中用于内部实现的功能,这部分不属于API,接口随时可能改变,不推荐使用。 */ public class AnnotationParser { @SuppressWarnings("serial") public static class ParseError extends UDFException { public ParseError(Throwable e) { super(e); } public ParseError(String e) { super(e); } } public static class Prototype { private OdpsType[] arguments; private OdpsType[] returns; public OdpsType[] getArguments() { return arguments; } public String getArgumentsString() { return convert2String(arguments); } public void setArguments(OdpsType[] arguments) { this.arguments = arguments; } public OdpsType[] getReturns() { return returns; } public String getReturnsString() { return convert2String(returns); } public void setReturns(OdpsType[] returns) { this.returns = returns; } private String convert2String(OdpsType[] types) { String[] s = new String[types.length]; for (int i = 0; i < types.length; i++) { s[i] = types[i].name().toLowerCase(); } return StringUtils.join(s, ','); } } public static class ParseResult { public boolean isVariadic() { return variadic; } public void setVariadic(boolean isVariadic) { this.variadic = isVariadic; } public List<Prototype> getProtoTypes() { return prototypes; } public void setTp(List<Prototype> tp) { this.prototypes = tp; } public boolean isWriable() { return writable; } public void setWritable(boolean val) { this.writable = val; } private boolean writable = false; private boolean variadic = false; private List<Prototype> prototypes = new ArrayList<Prototype>(); } public static ParseResult parse(Class<?> clz) throws ParseError { ParseResult result = new ParseResult(); Resolve r = clz.getAnnotation(Resolve.class); if (r == null) { throw new ParseError("@Resolve annotation not found."); } String[] infos = r.value(); for (String info : infos) { String errMsg = "@Resolve({\"" + info + "\"}) "; if (info.isEmpty()) { throw new ParseError(errMsg + "must not be empty string"); } int pos = info.indexOf("->"); String args = ""; if (pos > 0) { args = info.substring(0, pos); } else if (pos < 0) { throw new ParseError(errMsg); } String rtypes; int tPos = info.indexOf("->", pos + 2); if (tPos >= 0) { throw new ParseError(errMsg + "contains not exactly one '->'"); } rtypes = info.substring(pos + 2, info.length()); Prototype proto = new Prototype(); String[] tokens = getTypeInfos(args); if (tokens == null) { throw new ParseError(errMsg + "annotates wrong arguments '" + args + "'"); } proto.setArguments(createTypes(tokens)); if (rtypes.isEmpty()) { throw new ParseError(errMsg + "annotates no output types '" + args + "'"); } tokens = getTypeInfos(rtypes); if (tokens == null) { throw new ParseError(errMsg + "annotates wrong output types '" + rtypes + "'"); } proto.setReturns(createTypes(tokens)); result.getProtoTypes().add(proto); } PreferWritable wr = clz.getAnnotation(PreferWritable.class); result.setWritable(wr != null); return result; } private static Set<String> registeredTypes = new HashSet<String>(); static { registeredTypes.add(OdpsType.BIGINT.name()); registeredTypes.add(OdpsType.STRING.name()); registeredTypes.add(OdpsType.DOUBLE.name()); registeredTypes.add(OdpsType.BOOLEAN.name()); registeredTypes.add(OdpsType.DECIMAL.name()); registeredTypes.add(OdpsType.DATETIME.name()); } private static String[] getTypeInfos(String sig) { if (sig.isEmpty()) { return new String[0]; } String[] sigArray = StringUtils.splitPreserveAllTokens(sig.toUpperCase(), ','); for (String type : sigArray) { if (!registeredTypes.contains(type)) { return null; } } return sigArray; } private static OdpsType[] createTypes(String[] tokens) { OdpsType[] r = new OdpsType[tokens.length]; for (int i = 0; i < tokens.length; i++) { // XXX: This is really ugly r[i] = OdpsType.valueOf(tokens[i].toUpperCase()); } return r; } }