http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ClassAdUnParser.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ClassAdUnParser.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ClassAdUnParser.java
new file mode 100644
index 0000000..4689612
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ClassAdUnParser.java
@@ -0,0 +1,492 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.asterix.external.classad.Value.NumberFactor;
+import org.apache.asterix.om.base.AMutableDouble;
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.asterix.om.base.AMutableInt64;
+import org.apache.commons.lang3.mutable.MutableBoolean;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ClassAdUnParser {
+
+ // table of string representation of operators
+ public static final String[] opString = { "", " < ", " <= ", " != ", " == ", " >= ", " > ", " is ", " isnt ", " +",
+ " -", " + ", " - ", " * ", " / ", " % ", " !", " || ", " && ", " ~", " | ", " ^ ", " & ", " << ", " >> ",
+ " >>> ", " () ", " [] ", " ?: " };
+ protected static char delimiter = '\"';
+
+ /// Constructor
+ public ClassAdUnParser() {
+ }
+
+ // The default delimiter for strings is '\"'
+ // This can be changed to '\'' to unparse quoted attributes, with this function
+ public void setDelimiter(char delim) {
+ delimiter = delim;
+ }
+
+ /**
+ * Unparse a value
+ *
+ * @param buffer
+ * The string to unparse to
+ * @param val
+ * The value to unparse
+ * @throws HyracksDataException
+ */
+ public void unparse(AMutableCharArrayString buffer, Value val) throws HyracksDataException {
+ switch (val.getType()) {
+ case NULL_VALUE:
+ buffer.appendString("(null-value)");
+ break;
+
+ case STRING_VALUE: {
+ AMutableCharArrayString s = new AMutableCharArrayString();
+ val.isStringValue(s);
+ buffer.appendChar('"');
+ for (int i = 0; i < s.getLength(); i++) {
+ char ch = s.charAt(i);
+ if (ch == delimiter) {
+ if (delimiter == '\"') {
+ buffer.appendString("\\\"");
+ continue;
+ } else {
+ buffer.appendString("\\\'");
+ continue;
+ }
+ }
+ switch (ch) {
+ case '\b':
+ buffer.appendString("\\b");
+ continue;
+ case '\f':
+ buffer.appendString("\\f");
+ continue;
+ case '\n':
+ buffer.appendString("\\n");
+ continue;
+ case '\r':
+ buffer.appendString("\\r");
+ continue;
+ case '\t':
+ buffer.appendString("\\t");
+ continue;
+ case '\\':
+ buffer.appendString("\\\\");
+ continue;
+ case '\'':
+ buffer.appendString("\'");
+ continue;
+ case '\"':
+ buffer.appendString("\"");
+ continue;
+ default:
+ if (Character.isISOControl(ch)) {
+ // print octal representation
+ buffer.appendString(String.format("\\%03o", ch));
+ continue;
+ }
+ break;
+ }
+
+ buffer.appendChar(ch);
+ }
+ buffer.appendChar('"');
+ return;
+ }
+ case INTEGER_VALUE: {
+ AMutableInt64 i = new AMutableInt64(0);
+ val.isIntegerValue(i);
+ buffer.appendString(String.valueOf(i.getLongValue()));
+ return;
+ }
+ case REAL_VALUE: {
+ AMutableDouble real = new AMutableDouble(0);
+ val.isRealValue(real);
+ if (real.getDoubleValue() == 0.0) {
+ // It might be positive or negative and it's
+ // hard to tell. printf is good at telling though.
+ // We also want to print it with as few
+ // digits as possible, which is why we don't use the
+ // case below.
+ buffer.appendString(String.valueOf(real.getDoubleValue()));
+ } else if (Util.isNan(real.getDoubleValue())) {
+ buffer.appendString("real(\"NaN\")");
+ } else if (Util.isInf(real.getDoubleValue()) == -1) {
+ buffer.appendString("real(\"-INF\")");
+ } else if (Util.isInf(real.getDoubleValue()) == 1) {
+ buffer.appendString("real(\"INF\")");
+ } else {
+ buffer.appendString(String.format("%1.15E", real.getDoubleValue()));
+ }
+ return;
+ }
+ case BOOLEAN_VALUE: {
+ MutableBoolean b = new MutableBoolean();
+ val.isBooleanValue(b);
+ buffer.appendString(b.booleanValue() ? "true" : "false");
+ return;
+ }
+ case UNDEFINED_VALUE: {
+ buffer.appendString("undefined");
+ return;
+ }
+ case ERROR_VALUE: {
+ buffer.appendString("error");
+ return;
+ }
+ case ABSOLUTE_TIME_VALUE: {
+ ClassAdTime asecs = new ClassAdTime();
+ val.isAbsoluteTimeValue(asecs);
+
+ buffer.appendString("absTime(\"");
+ Util.absTimeToString(asecs, buffer);
+ buffer.appendString("\")");
+ return;
+ }
+ case RELATIVE_TIME_VALUE: {
+ ClassAdTime rsecs = new ClassAdTime();
+ val.isRelativeTimeValue(rsecs);
+ buffer.appendString("relTime(\"");
+ Util.relTimeToString(rsecs.getRelativeTime(), buffer);
+ buffer.appendString("\")");
+
+ return;
+ }
+ case CLASSAD_VALUE: {
+ ClassAd ad = new ClassAd();
+ Map<CaseInsensitiveString, ExprTree> attrs = new HashMap<CaseInsensitiveString, ExprTree>();
+ val.isClassAdValue(ad);
+ ad.getComponents(attrs);
+ unparseAux(buffer, attrs);
+ return;
+ }
+ case SLIST_VALUE:
+ case LIST_VALUE: {
+ ExprList el = new ExprList();
+ val.isListValue(el);
+ unparseAux(buffer, el);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Unparse an expression
+ *
+ * @param buffer
+ * The string to unparse to
+ * @param expr
+ * The expression to unparse
+ * @throws HyracksDataException
+ */
+ public void unparse(AMutableCharArrayString buffer, ExprTree tree) throws HyracksDataException {
+ if (tree == null) {
+ buffer.appendString("<error:null expr>");
+ return;
+ }
+
+ switch (tree.getKind()) {
+ case LITERAL_NODE: { // value
+ Value val = new Value();
+ AMutableNumberFactor factor = new AMutableNumberFactor();
+ ((Literal) tree.self()).getComponents(val, factor);
+ unparseAux(buffer, val, factor.getFactor());
+ return;
+ }
+
+ case ATTRREF_NODE: { // string
+ ExprTreeHolder expr = new ExprTreeHolder(); //needs initialization
+ AMutableCharArrayString ref = new AMutableCharArrayString();
+ MutableBoolean absolute = new MutableBoolean();
+ ((AttributeReference) tree.self()).getComponents(expr, ref, absolute);
+ unparseAux(buffer, expr, ref, absolute.booleanValue());
+ return;
+ }
+
+ case OP_NODE: { //string
+ AMutableInt32 op = new AMutableInt32(0);
+ ExprTreeHolder t1 = new ExprTreeHolder();
+ ExprTreeHolder t2 = new ExprTreeHolder();
+ ExprTreeHolder t3 = new ExprTreeHolder();
+ ((Operation) tree.self()).getComponents(op, t1, t2, t3);
+ unparseAux(buffer, op.getIntegerValue().intValue(), t1, t2, t3);
+ return;
+ }
+
+ case FN_CALL_NODE: { // string
+ AMutableCharArrayString fnName = new AMutableCharArrayString();
+ ExprList args = new ExprList();
+ ((FunctionCall) tree.self()).getComponents(fnName, args);
+ unparseAux(buffer, fnName, args);
+ return;
+ }
+
+ case CLASSAD_NODE: { // nested record
+ Map<CaseInsensitiveString, ExprTree> attrs = new HashMap<CaseInsensitiveString, ExprTree>();
+ ((ClassAd) tree.self()).getComponents(attrs);
+ unparseAux(buffer, attrs);
+ return;
+ }
+ case EXPR_LIST_NODE: { // list
+ ExprList exprs = new ExprList();
+ ((ExprList) tree.self()).getComponents(exprs);
+ unparseAux(buffer, exprs);
+ return;
+ }
+
+ default:
+ // I really wonder whether we should except here, but I
+ // don't want to do that without further consultation.
+ // wenger 2003-12-11.
+ buffer.setValue("");
+ throw new HyracksDataException("unknown expression type");
+ }
+ }
+
+ private void unparseAux(AMutableCharArrayString buffer, AMutableCharArrayString fnName, ExprList args)
+ throws HyracksDataException {
+ buffer.appendString(fnName);
+ buffer.appendChar('(');
+ for (ExprTree tree : args.getExprList()) {
+ unparse(buffer, tree);
+ buffer.appendChar(',');
+ }
+ if (args.size() > 0) {
+ buffer.decrementLength();
+ }
+ buffer.appendChar(')');
+
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, final Value value, NumberFactor numFactor)
+ throws HyracksDataException {
+ unparse(buffer, value);
+ if ((value.isIntegerValue() || value.isRealValue()) && numFactor != NumberFactor.NO_FACTOR) {
+ buffer.appendChar((numFactor == NumberFactor.B_FACTOR) ? 'B'
+ : (numFactor == NumberFactor.K_FACTOR) ? 'K'
+ : (numFactor == NumberFactor.M_FACTOR) ? 'M'
+ : (numFactor == NumberFactor.G_FACTOR) ? 'G'
+ : (numFactor == NumberFactor.T_FACTOR) ? 'T' : '?');
+ if (buffer.charAt(buffer.getLength() - 1) == '?') {
+ buffer.reset();
+ throw new HyracksDataException("bad number factor");
+ }
+ }
+ return;
+ }
+
+ /**
+ * @param buffer
+ * @param tree
+ * @param ref
+ * @param absolute
+ * = false if ommitted
+ * @throws HyracksDataException
+ */
+ public void unparseAux(AMutableCharArrayString buffer, final ExprTree tree, AMutableCharArrayString ref,
+ boolean absolute) throws HyracksDataException {
+
+ if (tree != null && tree.self() != null) {
+ unparse(buffer, tree);
+ buffer.appendChar('.');
+ buffer.appendString(ref);
+ return;
+ }
+ if (absolute) {
+ buffer.appendChar('.');
+ }
+ unparseAux(buffer, ref);
+ };
+
+ public void unparseAux(AMutableCharArrayString buffer, final ExprTree tree, AMutableCharArrayString ref)
+ throws HyracksDataException {
+ unparseAux(buffer, tree, ref, false);
+ };
+
+ public void unparseAuxPairs(AMutableCharArrayString buffer, List<Entry<AMutableCharArrayString, ExprTree>> attrlist)
+ throws HyracksDataException {
+ String delim = "; "; // NAC
+ buffer.appendString("[ ");
+ for (Entry<AMutableCharArrayString, ExprTree> entry : attrlist) {
+ unparseAux(buffer, entry.getKey());
+ buffer.appendString(" = ");
+ unparse(buffer, entry.getValue());
+ buffer.appendString(delim);
+ }
+ //get rid of last delimiter
+ buffer.setLength(buffer.getLength() - delim.length());
+ buffer.appendString(" ]");
+ }
+
+ // to unparse attribute names (quoted & unquoted attributes)
+ public void unparseAux(AMutableCharArrayString buffer, AMutableCharArrayString identifier)
+ throws HyracksDataException {
+ Value val = new Value();
+ AMutableCharArrayString idstr = new AMutableCharArrayString();
+
+ val.setStringValue(identifier);
+ setDelimiter('\''); // change the delimiter from string-literal mode to quoted attribute mode
+ unparse(idstr, val);
+ setDelimiter('\"'); // set delimiter back to default setting
+ idstr.erase(0, 1);
+ idstr.erase(idstr.length() - 1, 1);
+ if (identifierNeedsQuoting(idstr)) {
+ idstr.insert(0, "'");
+ idstr.appendString("'");
+ }
+ buffer.appendString(idstr);
+ }
+
+ static boolean identifierNeedsQuoting(AMutableCharArrayString aString) {
+ return false;
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, Value val, AMutableNumberFactor factor)
+ throws HyracksDataException {
+ unparse(buffer, val);
+ if ((val.isIntegerValue() || val.isRealValue()) && factor.getFactor() != NumberFactor.NO_FACTOR) {
+ buffer.appendString((factor.getFactor() == NumberFactor.B_FACTOR) ? "B"
+ : (factor.getFactor() == NumberFactor.K_FACTOR) ? "K"
+ : (factor.getFactor() == NumberFactor.M_FACTOR) ? "M"
+ : (factor.getFactor() == NumberFactor.G_FACTOR) ? "G"
+ : (factor.getFactor() == NumberFactor.T_FACTOR) ? "T"
+ : "<error:bad factor>");
+ }
+ return;
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, ExprTree expr, String attrName, boolean absolute)
+ throws HyracksDataException {
+ if (expr != null) {
+ unparse(buffer, expr);
+ buffer.appendString("." + attrName);
+ return;
+ }
+ if (absolute)
+ buffer.appendChar('.');
+ unparseAux(buffer, attrName);
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, int op, ExprTreeHolder t1, ExprTreeHolder t2,
+ ExprTreeHolder t3) throws HyracksDataException {
+ // case 0: parentheses op
+ if (op == Operation.OpKind_PARENTHESES_OP) {
+ buffer.appendString("( ");
+ unparse(buffer, t1);
+ buffer.appendString(" )");
+ return;
+ }
+ // case 1: check for unary ops
+ if (op == Operation.OpKind_UNARY_PLUS_OP || op == Operation.OpKind_UNARY_MINUS_OP
+ || op == Operation.OpKind_LOGICAL_NOT_OP || op == Operation.OpKind_BITWISE_NOT_OP) {
+ buffer.appendString(opString[op]);
+ unparse(buffer, t1);
+ return;
+ }
+ // case 2: check for ternary op
+ if (op == Operation.OpKind_TERNARY_OP) {
+ unparse(buffer, t1);
+ buffer.appendString(" ? ");
+ unparse(buffer, t2);
+ buffer.appendString(" : ");
+ unparse(buffer, t3);
+ return;
+ }
+ // case 3: check for subscript op
+ if (op == Operation.OpKind_SUBSCRIPT_OP) {
+ unparse(buffer, t1);
+ buffer.appendChar('[');
+ unparse(buffer, t2);
+ buffer.appendChar(']');
+ return;
+ }
+
+ // all others are binary ops
+ unparse(buffer, t1);
+ buffer.appendString(opString[op]);
+ unparse(buffer, t2);
+ }
+
+ public void UnparseAux(AMutableCharArrayString buffer, String fnName, ExprList args) throws HyracksDataException {
+ buffer.appendString(fnName + "(");
+ for (ExprTree tree : args.getExprList()) {
+ unparse(buffer, tree);
+ buffer.appendChar(',');
+ }
+ buffer.setChar(buffer.getLength() - 1, ')');
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, Map<CaseInsensitiveString, ExprTree> attrs)
+ throws HyracksDataException {
+
+ String delim = "; "; // NAC
+
+ buffer.appendString("[ ");
+
+ for (Entry<CaseInsensitiveString, ExprTree> entry : attrs.entrySet()) {
+ unparseAux(buffer, entry.getKey().get());
+ buffer.appendString(" = ");
+ unparse(buffer, entry.getValue());
+ buffer.appendString(delim); // NAC
+ }
+ buffer.setLength(buffer.getLength() - delim.length());
+ buffer.appendString(" ]");
+
+ }
+
+ public void unparseAux(AMutableCharArrayString buffer, ExprList exprs) throws HyracksDataException {
+
+ buffer.appendString("{ ");
+ for (ExprTree expr : exprs.getExprList()) {
+ unparse(buffer, expr);
+ buffer.appendChar(',');
+ }
+ buffer.decrementLength();
+ buffer.appendString(" }");
+ }
+
+ /* To unparse the identifier strings
+ * based on the character content,
+ * it's unparsed either as a quoted attribute or non-quoted attribute
+ */
+ public void unparseAux(AMutableCharArrayString buffer, String identifier) throws HyracksDataException {
+ Value val = new Value();
+ AMutableCharArrayString idstr = new AMutableCharArrayString();
+
+ val.setStringValue(identifier);
+ setDelimiter('\''); // change the delimiter from string-literal mode to quoted attribute mode
+ unparse(idstr, val);
+ setDelimiter('\"'); // set delimiter back to default setting
+ idstr.erase(0, 1);
+ idstr.erase(idstr.length() - 1, 1);
+ if (identifierNeedsQuoting(idstr)) {
+ idstr.prependChar('\'');
+ idstr.appendChar('\'');
+ }
+ buffer.appendString(idstr);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/Common.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/Common.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/Common.java
new file mode 100644
index 0000000..b3c027b
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/Common.java
@@ -0,0 +1,66 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.nio.charset.StandardCharsets;
+
+public class Common {
+ public static final String ATTR_AD = "Ad";
+ public static final String ATTR_CONTEXT = "Context";
+ public static final String ATTR_DEEP_MODS = "DeepMods";
+ public static final String ATTR_DELETE_AD = "DeleteAd";
+ public static final String ATTR_DELETES = "Deletes";
+ public static final String ATTR_KEY = "Key";
+ public static final String ATTR_NEW_AD = "NewAd";
+ public static final String ATTR_OP_TYPE = "OpType";
+ public static final String ATTR_PARENT_VIEW_NAME = "ParentViewName";
+ public static final String ATTR_PARTITION_EXPRS = "PartitionExprs";
+ public static final String ATTR_PARTITIONED_VIEWS = "PartitionedViews";
+ public static final String ATTR_PROJECT_THROUGH = "ProjectThrough";
+ public static final String ATTR_RANK_HINTS = "RankHints";
+ public static final String ATTR_REPLACE = "Replace";
+ public static final String ATTR_SUBORDINATE_VIEWS = "SubordinateViews";
+ public static final String ATTR_UPDATES = "Updates";
+ public static final String ATTR_WANT_LIST = "WantList";
+ public static final String ATTR_WANT_PRELUDE = "WantPrelude";
+ public static final String ATTR_WANT_RESULTS = "WantResults";
+ public static final String ATTR_WANT_POSTLUDE = "WantPostlude";
+ public static final String ATTR_VIEW_INFO = "ViewInfo";
+ public static final String ATTR_VIEW_NAME = "ViewName";
+ public static final String ATTR_XACTION_NAME = "XactionName";
+ public static final String ATTR_REQUIREMENTS = "Requirements";
+ public static final String ATTR_RANK = "Rank";
+
+ public static class CaseIgnLTStr {
+ public static boolean call(String s1, String s2) {
+ return (s1.compareToIgnoreCase(s2) < 0);
+ }
+ };
+
+ public static class ClassadAttrNameHash {
+ public static int call(String s) {
+ int h = 0;
+ byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
+ for (byte ch : bytes) {
+ h = 5 * h + (ch | 0x20);
+ }
+ return h;
+ }
+ };
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/EvalState.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/EvalState.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/EvalState.java
new file mode 100644
index 0000000..0719fd8
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/EvalState.java
@@ -0,0 +1,120 @@
+/*
+ * 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.asterix.external.classad;
+
+public class EvalState {
+
+ private int depthRemaining; // max recursion depth - current depth
+ // Normally, rootAd will be the ClassAd at the root of the tree
+ // of ExprTrees in the current evaluation. That is, the parent
+ // scope whose parent scope is NULL.
+ // It can be set to a closer parent scope. Then that ClassAd is
+ // treated like it has no parent scope for LookupInScope() and
+ // Evaluate().
+ private ClassAd rootAd;
+ private ClassAd curAd;
+ private boolean flattenAndInline; // NAC
+ private boolean inAttrRefScope;
+
+ public boolean isInAttrRefScope() {
+ return inAttrRefScope;
+ }
+
+ public void setFlattenAndInline(boolean flattenAndInline) {
+ this.flattenAndInline = flattenAndInline;
+ }
+
+ public void setInAttrRefScope(boolean inAttrRefScope) {
+ this.inAttrRefScope = inAttrRefScope;
+ }
+
+ public EvalState() {
+ rootAd = new ClassAd();
+ curAd = new ClassAd();
+ depthRemaining = ExprTree.MAX_CLASSAD_RECURSION;
+ flattenAndInline = false; // NAC
+ inAttrRefScope = false;
+ }
+
+ public boolean isFlattenAndInline() {
+ return flattenAndInline;
+ }
+
+ public void setScopes(ClassAd curScope) {
+ curAd = curScope;
+ setRootScope();
+ }
+
+ public void setRootScope() {
+ ClassAd prevScope = curAd;
+ if (curAd == null) {
+ rootAd = null;
+ } else {
+ ClassAd curScope = curAd.getParentScope();
+
+ while (curScope != null) {
+ if (curScope == curAd) { // NAC - loop detection
+ rootAd = null;
+ return; // NAC
+ } // NAC
+ prevScope = curScope;
+ curScope = curScope.getParentScope();
+ }
+
+ rootAd = prevScope;
+ }
+ return;
+ }
+
+ public void reset() {
+ rootAd.reset();
+ curAd.reset();
+ depthRemaining = ExprTree.MAX_CLASSAD_RECURSION;
+ flattenAndInline = false;
+ inAttrRefScope = false;
+ }
+
+ public ClassAd getRootAd() {
+ return rootAd;
+ }
+
+ public ClassAd getCurAd() {
+ return curAd;
+ }
+
+ public void setCurAd(ClassAd curAd) {
+ this.curAd = curAd;
+ }
+
+ public int getDepthRemaining() {
+ return depthRemaining;
+ }
+
+ public void decrementDepth() {
+ depthRemaining--;
+ }
+
+ public void incrementDepth() {
+ depthRemaining++;
+ }
+
+ public void setRootAd(ClassAd classAd) {
+ this.rootAd = classAd;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprList.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprList.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprList.java
new file mode 100644
index 0000000..13ef3c1
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprList.java
@@ -0,0 +1,280 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ExprList extends ExprTree {
+
+ private List<ExprTree> exprList;
+ private EvalState state = new EvalState();
+ public boolean isShared = false;
+
+ public boolean copyFrom(ExprList exprList) throws HyracksDataException {
+ this.exprList.clear();
+ for (ExprTree expr : exprList.exprList) {
+ this.exprList.add(expr.copy());
+ }
+ return true;
+ }
+
+ public int getlast() {
+ return exprList == null ? 0 : exprList.size() - 1;
+ }
+
+ public ExprTree get(int i) {
+ return exprList.get(i);
+ }
+
+ @Override
+ public int size() {
+ return exprList == null ? 0 : exprList.size();
+ }
+
+ // called from FunctionCall
+ @Override
+ public void privateSetParentScope(ClassAd scope) {
+ for (ExprTree tree : exprList) {
+ tree.setParentScope(scope);
+ }
+ }
+
+ @Override
+ public NodeKind getKind() {
+ return NodeKind.EXPR_LIST_NODE;
+ }
+
+ public List<ExprTree> getExprList() {
+ return exprList;
+ }
+
+ public Iterator<ExprTree> iterator() {
+ return exprList.iterator();
+ }
+
+ public void setValue(ExprList value) throws HyracksDataException {
+ if (value == null) {
+ clear();
+ } else {
+ copyFrom(value);
+ }
+ }
+
+ public void add(ExprTree expr) {
+ exprList.add(expr.self());
+ }
+
+ public void setExprList(List<ExprTree> exprList) {
+ this.exprList = exprList;
+ }
+
+ public ExprList(List<ExprTree> exprs) {
+ exprList = new ArrayList<ExprTree>();
+ copyList(exprs);
+ return;
+ }
+
+ public ExprList(ExprList other_list) throws HyracksDataException {
+ exprList = new ArrayList<ExprTree>();
+ copyFrom(other_list);
+ return;
+ }
+
+ public ExprList() {
+ exprList = new ArrayList<ExprTree>();
+ }
+
+ public ExprList(boolean b) {
+ this.exprList = new ArrayList<ExprTree>();
+ this.isShared = b;
+ }
+
+ public void clear() {
+ exprList.clear();
+ }
+
+ @Override
+ public ExprTree copy() throws HyracksDataException {
+ ExprList newList = new ExprList();
+ newList.copyFrom(this);
+ return newList;
+ }
+
+ @Override
+ public boolean sameAs(ExprTree tree) {
+ boolean is_same;
+ if (this == tree) {
+ is_same = true;
+ } else if (tree.getKind() != NodeKind.EXPR_LIST_NODE) {
+ is_same = false;
+ } else {
+ ExprList other_list = (ExprList) tree;
+ if (exprList.size() != other_list.size()) {
+ is_same = false;
+ } else {
+ is_same = true;
+ for (int i = 0; i < exprList.size(); i++) {
+ if (!exprList.get(i).sameAs(other_list.get(i))) {
+ is_same = false;
+ break;
+ }
+ }
+ }
+ }
+ return is_same;
+ }
+
+ public static ExprList createExprList(List<ExprTree> exprs) {
+ ExprList el = new ExprList();
+ el.copyList(exprs);
+ return el;
+ }
+
+ public static ExprList createExprList(ExprList exprs) {
+ ExprList el = new ExprList();
+ el.copyList(exprs.exprList);
+ return el;
+ }
+
+ public void getComponents(List<ExprTree> exprs) {
+ exprs.clear();
+ exprs.addAll(exprList);
+ }
+
+ public void getComponents(ExprList list) throws HyracksDataException {
+ list.clear();
+ list.addAll(exprList);
+ /*
+ for(ExprTree e: exprList){
+ list.add(e.Copy());
+ }*/
+ }
+
+ private void addAll(List<ExprTree> exprList) {
+ this.exprList.addAll(exprList);
+ }
+
+ public void insert(ExprTree t) {
+ exprList.add(t);
+ }
+
+ public void push_back(ExprTree t) {
+ exprList.add(t);
+ }
+
+ public void erase(int f, int to) {
+ int listInitialSize = exprList.size();
+ Iterator<ExprTree> it = exprList.iterator();
+ int i = 0;
+ while (i < listInitialSize && i < to) {
+ it.next();
+ if (i >= f) {
+ it.remove();
+ }
+ i++;
+ }
+ return;
+ }
+
+ public void erase(int index) {
+ exprList.remove(index);
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value val) throws HyracksDataException {
+ val.setListValue(this);
+ return (true);
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value val, ExprTreeHolder sig) throws HyracksDataException {
+ val.setListValue(this);
+ sig.setInnerTree(copy());
+ return (sig.getInnerTree() != null);
+ }
+
+ @Override
+ public boolean privateFlatten(EvalState state, Value val, ExprTreeHolder tree, AMutableInt32 aInt)
+ throws HyracksDataException {
+ ExprTreeHolder nexpr = new ExprTreeHolder();
+ Value tempVal = new Value();
+ ExprList newList = new ExprList();
+
+ tree.setInnerTree(null);; // Just to be safe... wenger 2003-12-11.
+
+ for (ExprTree expr : exprList) {
+ // flatten the constituent expression
+ if (!expr.publicFlatten(state, tempVal, nexpr)) {
+ return false;
+ }
+ // if only a value was obtained, convert to an expression
+ if (nexpr.getInnerTree() == null) {
+ nexpr.setInnerTree(Literal.createLiteral(tempVal));
+ if (nexpr.getInnerTree() == null) {
+ return false;
+ }
+ }
+ // add the new expression to the flattened list
+ newList.push_back(nexpr);
+ }
+ tree.setInnerTree(newList);
+ return true;
+ }
+
+ public void copyList(List<ExprTree> exprs) {
+ for (ExprTree expr : exprs) {
+ exprList.add(expr);
+ }
+ }
+
+ public boolean getValue(Value val, ExprTree tree, EvalState es) throws HyracksDataException {
+ EvalState currentState = new EvalState();
+
+ if (tree == null)
+ return false;
+
+ // if called from user code, es == NULL so we use &state instead
+ currentState = (es != null) ? es : state;
+
+ if (currentState.getDepthRemaining() <= 0) {
+ val.setErrorValue();
+ return false;
+ }
+ currentState.decrementDepth();
+
+ ClassAd tmpScope = currentState.getCurAd();
+ currentState.setCurAd(tree.getParentScope());
+ tree.publicEvaluate(currentState, val);
+ currentState.setCurAd(tmpScope);
+
+ currentState.incrementDepth();
+
+ return true;
+ }
+
+ @Override
+ public void reset() {
+ exprList.clear();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTree.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTree.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTree.java
new file mode 100644
index 0000000..ccbfd8b
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTree.java
@@ -0,0 +1,401 @@
+/*
+ * 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.asterix.external.classad;
+
+import org.apache.asterix.om.base.AMutableDouble;
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.asterix.om.base.AMutableInt64;
+import org.apache.commons.lang3.mutable.MutableBoolean;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+/**
+ * A node of the expression tree, which may be a literal, attribute reference,
+ * function call, classad, expression list, or an operator applied to other
+ * ExprTree operands.
+ */
+public abstract class ExprTree {
+
+ /// The kinds of nodes in expression trees
+ public enum NodeKind {
+ /// Literal node (string, integer, real, boolean, undefined, error)
+ LITERAL_NODE,
+ /// Attribute reference node (attr, .attr, expr.attr)
+ ATTRREF_NODE,
+ /// Expression operation node (unary, binary, ternary)/
+ OP_NODE,
+ /// Function call node
+ FN_CALL_NODE,
+ /// ClassAd node
+ CLASSAD_NODE,
+ /// Expression list node
+ EXPR_LIST_NODE,
+ /// Expression envelope.
+ EXPR_ENVELOPE
+ }
+
+ public enum EvalResult {
+ EVAL_FAIL,
+ EVAL_OK,
+ EVAL_UNDEF,
+ EVAL_ERROR
+ };
+
+ public static final int EVAL_FAIL_Int = 0;
+ public static final int EVAL_OK_Int = 1;
+ public static final int EVAL_UNDEF_Int = 2;
+ public static final int EVAL_ERROR_Int = 3;
+
+ public static final int MAX_CLASSAD_RECURSION = 1000;
+
+ public boolean isTreeHolder() {
+ return false;
+ }
+
+ public int size;
+ public ClassAd parentScope;
+
+ private CallableDebugFunction userDebugFunction;
+
+ public abstract void reset();
+
+ public ExprTree() {
+ this.parentScope = null;
+ this.size = 0;
+ }
+
+ public ExprTree(ExprTree expr) {
+ this.size = expr.size;
+ }
+
+ public void resetExprTree(ExprTree expr) {
+ if (expr == null) {
+ this.size = 0;
+ } else {
+ this.size = expr.size;
+ }
+ }
+
+ public static class ExprHash {
+ public static int call(ExprTree x) {
+ return x.size;
+ }
+ }
+
+ public ExprTree getTree() {
+ return this;
+ }
+
+ /**
+ * Sets the lexical parent scope of the expression, which is used to
+ * determine the lexical scoping structure for resolving attribute
+ * references. (However, the semantic parent may be different from
+ * the lexical parent if a <tt>super</tt> attribute is specified.)
+ * This method is automatically called when expressions are
+ * inserted into ClassAds, and should thus be called explicitly
+ * only when evaluating expressions which haven't been inserted
+ * into a ClassAd.
+ */
+ public void setParentScope(ClassAd scope) {
+ if (scope == null) {
+ parentScope = null;
+ return;
+ }
+ if (parentScope == null) {
+ parentScope = new ClassAd();
+ }
+ parentScope.setValue(scope);
+ privateSetParentScope(scope);
+ }
+
+ abstract protected void privateSetParentScope(ClassAd scope);
+
+ /**
+ * Gets the parent scope of the expression.
+ *
+ * @return The parent scope of the expression.
+ */
+ public ClassAd getParentScope() {
+ return parentScope;
+ }
+
+ /**
+ * Makes a deep copy of the expression tree
+ *
+ * @return A deep copy of the expression, or NULL on failure.
+ * @throws HyracksDataException
+ */
+
+ public abstract ExprTree copy() throws HyracksDataException;
+
+ /**
+ * Gets the node kind of this expression node.
+ *
+ * @return The node kind. Child nodes MUST implement this.
+ * @see NodeKind
+ */
+ public abstract NodeKind getKind();
+
+ /**
+ * To eliminate the mass of external checks to see if the ExprTree is
+ * a classad.
+ */
+ public static boolean isClassAd(Object o) {
+ return (o instanceof ClassAd);
+ }
+
+ /**
+ * Return a ptr to the raw exprtree below the interface
+ */
+
+ public ExprTree self() {
+ return this;
+ }
+
+ /// A debugging method; send expression to stdout
+ public void puke() throws HyracksDataException {
+ PrettyPrint unp = new PrettyPrint();
+ AMutableCharArrayString buffer = new AMutableCharArrayString();
+ unp.unparse(buffer, this);
+ System.out.println(buffer.toString());
+ }
+
+ //Pass in a pointer to a function taking a const char *, which will
+ //print it out somewhere useful, when the classad debug() function
+ //is called
+ public void setUserDebugFunction(CallableDebugFunction dbf) {
+ this.userDebugFunction = dbf;
+ }
+
+ public void debugPrint(String message) {
+ if (userDebugFunction != null) {
+ userDebugFunction.call(message);
+ }
+ }
+
+ public void debugFormatValue(Value value) throws HyracksDataException {
+ debugFormatValue(value, 0.0);
+ }
+
+ public void debugFormatValue(Value value, double time) throws HyracksDataException {
+ MutableBoolean boolValue = new MutableBoolean(false);
+ AMutableInt64 intValue = new AMutableInt64(0);
+ AMutableDouble doubleValue = new AMutableDouble(0.0);
+ AMutableCharArrayString stringValue = new AMutableCharArrayString();
+
+ if (NodeKind.CLASSAD_NODE == getKind())
+ return;
+
+ PrettyPrint unp = new PrettyPrint();
+ AMutableCharArrayString buffer = new AMutableCharArrayString();
+ unp.unparse(buffer, this);
+
+ String result = "Classad debug: ";
+ if (time != 0) {
+ String buf = String.format("%5.5fms", time * 1000);
+ result += "[";
+ result += buf;
+ result += "] ";
+ }
+ result += buffer;
+ result += " --> ";
+
+ switch (value.getType()) {
+ case NULL_VALUE:
+ result += "NULL\n";
+ break;
+ case ERROR_VALUE:
+ if ((NodeKind.FN_CALL_NODE == getKind()) && !((FunctionCall) (this)).functionIsDefined()) {
+ result += "ERROR (function is not defined)\n";
+ } else {
+ result += "ERROR\n";
+ }
+ break;
+ case UNDEFINED_VALUE:
+ result += "UNDEFINED\n";
+ break;
+ case BOOLEAN_VALUE:
+ if (value.isBooleanValue(boolValue))
+ result += boolValue.booleanValue() ? "TRUE\n" : "FALSE\n";
+ break;
+ case INTEGER_VALUE:
+ if (value.isIntegerValue(intValue)) {
+ result += String.format("%lld", intValue.getLongValue());
+ result += "\n";
+ }
+ break;
+
+ case REAL_VALUE:
+ if (value.isRealValue(doubleValue)) {
+ result += String.format("%lld", doubleValue.getDoubleValue());
+ result += "\n";
+ }
+ break;
+ case RELATIVE_TIME_VALUE:
+ result += "RELATIVE TIME\n";
+ break;
+ case ABSOLUTE_TIME_VALUE:
+ result += "ABSOLUTE TIME\n";
+ break;
+ case STRING_VALUE:
+ if (value.isStringValue(stringValue)) {
+ result += stringValue.toString();
+ result += "\n";
+ }
+ break;
+ case CLASSAD_VALUE:
+ result += "CLASSAD\n";
+ break;
+ case LIST_VALUE:
+ result += "LIST\n";
+ break;
+ case SLIST_VALUE:
+ result += "SLIST\n";
+ break;
+ }
+ debugPrint(result);
+ }
+
+ /**
+ * Evaluate this tree
+ *
+ * @param state
+ * The current state
+ * @param val
+ * The result of the evaluation
+ * @return true on success, false on failure
+ * @throws HyracksDataException
+ */
+
+ public boolean publicEvaluate(EvalState state, Value val) throws HyracksDataException {
+ return privateEvaluate(state, val);
+ }
+
+ public boolean publicEvaluate(EvalState state, Value val, ExprTreeHolder sig) throws HyracksDataException {
+ return privateEvaluate(state, val, sig);
+ }
+
+ public abstract boolean privateEvaluate(EvalState state, Value val) throws HyracksDataException;
+
+ public abstract boolean privateEvaluate(EvalState state, Value val, ExprTreeHolder tree)
+ throws HyracksDataException;
+
+ /**
+ * Evaluate this tree.
+ * This only works if the expression is currently part of a ClassAd.
+ *
+ * @param val
+ * The result of the evaluation
+ * @return true on success, false on failure
+ * @throws HyracksDataException
+ */
+ public boolean publicEvaluate(Value val) throws HyracksDataException {
+ EvalState state = new EvalState();
+ if (parentScope == null) {
+ val.setErrorValue();
+ return false;
+ } else {
+ state.setScopes(parentScope);
+ return (publicEvaluate(state, val));
+ }
+ }
+
+ /**
+ * Is this ExprTree the same as the tree?
+ *
+ * @return true if it is the same, false otherwise
+ */
+ public abstract boolean sameAs(ExprTree tree);
+
+ /**
+ * Fill in this ExprTree with the contents of the other ExprTree.
+ *
+ * @return true if the copy succeeded, false otherwise.
+ * @throws HyracksDataException
+ */
+ public void copyFrom(ExprTree tree) throws HyracksDataException {
+ if (!this.equals(tree)) {
+ parentScope = tree.parentScope;
+ }
+ return;
+ }
+
+ public interface CallableDebugFunction {
+ public void call(String message);
+ }
+
+ public boolean publicEvaluate(Value val, ExprTreeHolder sig) throws HyracksDataException {
+ EvalState state = new EvalState();
+ state.setScopes(parentScope);
+ return (publicEvaluate(state, val, sig));
+ }
+
+ public boolean publicFlatten(Value val, ExprTreeHolder tree) throws HyracksDataException {
+ EvalState state = new EvalState();
+ state.setScopes(parentScope);
+ return (publicFlatten(state, val, tree));
+ }
+
+ public boolean publicFlatten(EvalState state, Value val, ExprTreeHolder tree, AMutableInt32 op)
+ throws HyracksDataException {
+ return (privateFlatten(state, val, tree, op));
+ }
+
+ public boolean publicFlatten(EvalState state, Value val, ExprTreeHolder tree) throws HyracksDataException {
+ return (privateFlatten(state, val, tree, null));
+ }
+
+ public abstract boolean privateFlatten(EvalState state, Value val, ExprTreeHolder tree, AMutableInt32 op)
+ throws HyracksDataException;
+
+ public boolean isClassad(ClassAd ptr) {
+ return (ptr instanceof ClassAd);
+ }
+
+ public int exprHash(ExprTree expr, int numBkts) {
+ int result = expr.getKind().ordinal() + 1000;
+ result += numBkts * (3 / 2);
+ return (result % numBkts);
+ }
+
+ @Override
+ public String toString() {
+ ClassAdUnParser unparser = new PrettyPrint();
+ AMutableCharArrayString string_representation = new AMutableCharArrayString();
+
+ try {
+ unparser.unparse(string_representation, this);
+ } catch (HyracksDataException e) {
+ e.printStackTrace();
+ }
+ return string_representation.toString();
+
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ExprTree) {
+ return sameAs((ExprTree) o);
+ }
+ return false;
+ }
+
+ public int size() {
+ return size;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTreeHolder.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTreeHolder.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTreeHolder.java
new file mode 100644
index 0000000..89c5c0b
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/ExprTreeHolder.java
@@ -0,0 +1,144 @@
+/*
+ * 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.asterix.external.classad;
+
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ExprTreeHolder extends ExprTree {
+ private ExprTree innerTree;
+
+ @Override
+ public ClassAd getParentScope() {
+ return innerTree.parentScope;
+ }
+
+ @Override
+ public void copyFrom(ExprTree tree) throws HyracksDataException {
+ if (tree == null) {
+ innerTree = null;
+ } else {
+ if (tree.isTreeHolder()) {
+ tree = ((ExprTreeHolder) tree).innerTree;
+ }
+ if (innerTree == null) {
+ innerTree = tree.copy();
+ } else {
+ innerTree.copyFrom(tree);
+ }
+ }
+ }
+
+ @Override
+ public void reset() {
+ this.innerTree = null;
+ }
+
+ @Override
+ public void puke() throws HyracksDataException {
+ PrettyPrint unp = new PrettyPrint();
+ AMutableCharArrayString buffer = new AMutableCharArrayString();
+ unp.unparse(buffer, innerTree);
+ System.out.println(buffer.toString());
+ }
+
+ @Override
+ public void resetExprTree(ExprTree expr) {
+ setInnerTree(expr);
+ }
+
+ @Override
+ public ExprTree getTree() {
+ return innerTree;
+ }
+
+ @Override
+ public ExprTree self() {
+ return innerTree;
+ }
+
+ @Override
+ public boolean isTreeHolder() {
+ return true;
+ }
+
+ public ExprTreeHolder() {
+ innerTree = null;
+ }
+
+ public ExprTreeHolder(ExprTree tree) {
+ setInnerTree(tree);
+ }
+
+ public ExprTree getInnerTree() {
+ return innerTree;
+ }
+
+ public void setInnerTree(ExprTree innerTree) {
+ if (innerTree != null && innerTree.isTreeHolder()) {
+ setInnerTree(((ExprTreeHolder) innerTree).getInnerTree());
+ } else {
+ this.innerTree = innerTree;
+ }
+ }
+
+ @Override
+ public ExprTree copy() throws HyracksDataException {
+ return innerTree.copy();
+ }
+
+ @Override
+ public NodeKind getKind() {
+ return innerTree.getKind();
+ }
+
+ @Override
+ public boolean sameAs(ExprTree tree) {
+ if (tree == null) {
+ return innerTree == null;
+ }
+ return innerTree == null ? false : innerTree.sameAs(tree);
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value val) throws HyracksDataException {
+ return innerTree.privateEvaluate(state, val);
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value val, ExprTreeHolder tree) throws HyracksDataException {
+ return innerTree.privateEvaluate(state, val, tree);
+ }
+
+ @Override
+ public boolean privateFlatten(EvalState state, Value val, ExprTreeHolder tree, AMutableInt32 op)
+ throws HyracksDataException {
+ return innerTree.privateFlatten(state, val, tree, op);
+ }
+
+ @Override
+ public int size() {
+ return innerTree != null ? 1 : 0;
+ }
+
+ @Override
+ protected void privateSetParentScope(ClassAd scope) {
+ innerTree.privateSetParentScope(scope);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FileLexerSource.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FileLexerSource.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FileLexerSource.java
new file mode 100644
index 0000000..cfa8932
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FileLexerSource.java
@@ -0,0 +1,89 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+public class FileLexerSource extends LexerSource {
+ // This source allows input from a file
+ private BufferedReader reader;
+ private boolean unread;
+ private boolean finished;
+ private boolean nextRead;
+ private char nextChar;
+
+ public FileLexerSource(File file) throws IOException {
+ this.reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8);
+ }
+
+ public void setNewSource(File file) throws IOException {
+ if (this.reader != null) {
+ reader.close();
+ }
+ this.reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public char readCharacter() throws IOException {
+ if (unread) {
+ unread = false;
+ return previousCharacter;
+ } else if (nextRead) {
+ nextRead = false;
+ return nextChar;
+ }
+ previousCharacter = (char) reader.read();
+ return previousCharacter;
+ }
+
+ @Override
+ public void unreadCharacter() throws IOException {
+ if (nextRead) {
+ throw new IOException("Unexpected Situation");
+ } else if (unread) {
+ throw new IOException("This lexer source supports only one step back");
+ }
+ unread = true;
+ }
+
+ @Override
+ public boolean atEnd() throws IOException {
+ if (finished) {
+ return true;
+ } else if (nextRead) {
+ return false;
+ }
+ nextChar = (char) reader.read();
+ if (nextChar < 0) {
+ finished = true;
+ } else {
+ nextRead = true;
+ }
+ return finished;
+ }
+
+ @Override
+ public char[] getBuffer() {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FunctionCall.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FunctionCall.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FunctionCall.java
new file mode 100644
index 0000000..bbc0e7a
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/FunctionCall.java
@@ -0,0 +1,354 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.util.HashMap;
+
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.asterix.om.base.AMutableString;
+import org.apache.commons.lang3.mutable.MutableBoolean;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class FunctionCall extends ExprTree {
+
+ public static boolean initialized = false;
+
+ public static final ClassAdFunc[] ClassAdBuiltinFunc = { BuiltinClassAdFunctions.IsType,
+ BuiltinClassAdFunctions.TestMember, BuiltinClassAdFunctions.Size, BuiltinClassAdFunctions.SumAvg,
+ BuiltinClassAdFunctions.MinMax, BuiltinClassAdFunctions.ListCompare, BuiltinClassAdFunctions.debug,
+ BuiltinClassAdFunctions.formatTime, BuiltinClassAdFunctions.getField, BuiltinClassAdFunctions.currentTime,
+ BuiltinClassAdFunctions.timeZoneOffset, BuiltinClassAdFunctions.splitTime, BuiltinClassAdFunctions.dayTime,
+ BuiltinClassAdFunctions.epochTime, BuiltinClassAdFunctions.strCat, BuiltinClassAdFunctions.changeCase,
+ BuiltinClassAdFunctions.subString, BuiltinClassAdFunctions.convInt, BuiltinClassAdFunctions.compareString,
+ BuiltinClassAdFunctions.matchPattern, BuiltinClassAdFunctions.matchPatternMember,
+ BuiltinClassAdFunctions.substPattern, BuiltinClassAdFunctions.convReal, BuiltinClassAdFunctions.convString,
+ BuiltinClassAdFunctions.unparse, BuiltinClassAdFunctions.convBool, BuiltinClassAdFunctions.convTime,
+ BuiltinClassAdFunctions.doRound, BuiltinClassAdFunctions.doMath2, BuiltinClassAdFunctions.random,
+ BuiltinClassAdFunctions.ifThenElse, BuiltinClassAdFunctions.stringListsIntersect,
+ BuiltinClassAdFunctions.interval, BuiltinClassAdFunctions.eval };
+
+ // function call specific information
+ private String functionName;
+ private ClassAdFunc function;
+ private ExprList arguments;
+ public static final HashMap<String, ClassAdFunc> funcTable = new HashMap<String, ClassAdFunc>();
+
+ static {
+ // load up the function dispatch table
+ // type predicates
+ funcTable.put("isundefined", BuiltinClassAdFunctions.IsType);
+ funcTable.put("iserror", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isstring", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isinteger", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isreal", BuiltinClassAdFunctions.IsType);
+ funcTable.put("islist", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isclassad", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isboolean", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isabstime", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isreltime", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isundefined", BuiltinClassAdFunctions.IsType);
+ funcTable.put("isundefined", BuiltinClassAdFunctions.IsType);
+ // list membership
+ funcTable.put("member", BuiltinClassAdFunctions.TestMember);
+ funcTable.put("identicalmember", BuiltinClassAdFunctions.TestMember);
+ // Some list functions, useful for lists as sets
+ funcTable.put("size", BuiltinClassAdFunctions.Size);
+ funcTable.put("sum", BuiltinClassAdFunctions.SumAvg);
+ funcTable.put("avg", BuiltinClassAdFunctions.SumAvg);
+ funcTable.put("min", BuiltinClassAdFunctions.MinMax);
+ funcTable.put("max", BuiltinClassAdFunctions.MinMax);
+ funcTable.put("anycompare", BuiltinClassAdFunctions.ListCompare);
+ funcTable.put("allcompare", BuiltinClassAdFunctions.ListCompare);
+ //basic functions
+ /*
+ funcTable.put("sumfrom", BuiltinFunctions.SumAvgFrom);
+ funcTable.put("avgfrom", BuiltinFunctions.SumAvgFrom);
+ funcTable.put("maxfrom", BuiltinFunctions.BoundFrom);
+ funcTable.put("minfrom", BuiltinFunctions.BoundFrom);
+ */
+ // time management
+ funcTable.put("time", BuiltinClassAdFunctions.epochTime);
+ funcTable.put("currenttime", BuiltinClassAdFunctions.currentTime);
+ funcTable.put("timezoneoffset", BuiltinClassAdFunctions.timeZoneOffset);
+ funcTable.put("daytime", BuiltinClassAdFunctions.dayTime);
+ funcTable.put("getyear", BuiltinClassAdFunctions.getField);
+ funcTable.put("getmonth", BuiltinClassAdFunctions.getField);
+ funcTable.put("getdayofyear", BuiltinClassAdFunctions.getField);
+ funcTable.put("getdayofmonth", BuiltinClassAdFunctions.getField);
+ funcTable.put("getdayofweek", BuiltinClassAdFunctions.getField);
+ funcTable.put("getdays", BuiltinClassAdFunctions.getField);
+ funcTable.put("gethours", BuiltinClassAdFunctions.getField);
+ funcTable.put("getminutes", BuiltinClassAdFunctions.getField);
+ funcTable.put("getseconds", BuiltinClassAdFunctions.getField);
+ funcTable.put("splittime", BuiltinClassAdFunctions.splitTime);
+ funcTable.put("formattime", BuiltinClassAdFunctions.formatTime);
+ // string manipulation
+ funcTable.put("strcat", BuiltinClassAdFunctions.strCat);
+ funcTable.put("toupper", BuiltinClassAdFunctions.changeCase);
+ funcTable.put("tolower", BuiltinClassAdFunctions.changeCase);
+ funcTable.put("substr", BuiltinClassAdFunctions.subString);
+ funcTable.put("strcmp", BuiltinClassAdFunctions.compareString);
+ funcTable.put("stricmp", BuiltinClassAdFunctions.compareString);
+ // pattern matching (regular expressions)
+ funcTable.put("regexp", BuiltinClassAdFunctions.matchPattern);
+ funcTable.put("regexpmember", BuiltinClassAdFunctions.matchPatternMember);
+ funcTable.put("regexps", BuiltinClassAdFunctions.substPattern);
+ // conversion functions
+ funcTable.put("int", BuiltinClassAdFunctions.convInt);
+ funcTable.put("real", BuiltinClassAdFunctions.convReal);
+ funcTable.put("string", BuiltinClassAdFunctions.convString);
+ funcTable.put("bool", BuiltinClassAdFunctions.convBool);
+ funcTable.put("abstime", BuiltinClassAdFunctions.convTime);
+ funcTable.put("reltime", BuiltinClassAdFunctions.convTime);
+
+ // turn the contents of an expression into a string
+ // but *do not* evaluate it
+
+ funcTable.put("unparse", BuiltinClassAdFunctions.unparse);
+ // mathematical functions
+ funcTable.put("floor", BuiltinClassAdFunctions.doRound);
+ funcTable.put("ceil", BuiltinClassAdFunctions.doRound);
+ funcTable.put("ceiling", BuiltinClassAdFunctions.doRound);
+ funcTable.put("round", BuiltinClassAdFunctions.doRound);
+ funcTable.put("pow", BuiltinClassAdFunctions.doMath2);
+ funcTable.put("quantize", BuiltinClassAdFunctions.doMath2);
+ funcTable.put("random", BuiltinClassAdFunctions.random);
+
+ // for compatibility with old classads:
+ funcTable.put("ifthenelse", BuiltinClassAdFunctions.ifThenElse);
+ funcTable.put("interval", BuiltinClassAdFunctions.interval);
+ funcTable.put("eval", BuiltinClassAdFunctions.eval);
+
+ // string list functions:
+ // Note that many other string list functions are defined
+ // externally in the Condor classad compatibility layer.
+
+ funcTable.put("stringlistsintersect", BuiltinClassAdFunctions.stringListsIntersect);
+ funcTable.put("debug", BuiltinClassAdFunctions.debug);
+ initialized = true;
+ }
+
+ /**
+ * Returns true if the function expression points to a valid
+ * function in the ClassAd library.
+ */
+ public boolean functionIsDefined() {
+ return function != null;
+ }
+
+ public void copyFrom(FunctionCall copiedFrom) throws HyracksDataException {
+ this.function = copiedFrom.function;
+ this.functionName = copiedFrom.functionName;
+ if (this.arguments == null) {
+ this.arguments = (ExprList) copiedFrom.arguments.copy();
+ } else {
+ this.arguments.copyFrom(copiedFrom.arguments);
+ }
+ }
+
+ public FunctionCall() {
+ functionName = null;
+ function = null;
+ arguments = null;
+ }
+
+ public static FunctionCall createFunctionCall(String functionName, ExprList args) {
+ FunctionCall fc = new FunctionCall();
+ fc.function = funcTable.get(functionName.toLowerCase());
+ fc.functionName = functionName;
+ fc.arguments = args;
+ return fc;
+ }
+
+ // start up with an argument list of size 4
+
+ public FunctionCall(FunctionCall functioncall) throws HyracksDataException {
+ copyFrom(functioncall);
+ }
+
+ @Override
+ public ExprTree copy() throws HyracksDataException {
+ FunctionCall newTree = new FunctionCall();
+ newTree.copyFrom(this);
+ return newTree;
+ }
+
+ @Override
+ public void copyFrom(ExprTree tree) throws HyracksDataException {
+ FunctionCall functioncall = (FunctionCall) tree;
+ functionName = functioncall.functionName;
+ function = functioncall.function;
+ arguments.copyFrom(arguments);
+ super.copyFrom(functioncall);
+ }
+
+ @Override
+ public boolean sameAs(ExprTree tree) {
+ boolean is_same = false;
+ FunctionCall other_fn;
+ ExprTree pSelfTree = tree.self();
+
+ if (this == pSelfTree) {
+ is_same = true;
+ } else if (pSelfTree.getKind() != NodeKind.FN_CALL_NODE) {
+ is_same = false;
+ } else {
+ other_fn = (FunctionCall) pSelfTree;
+ if (functionName == other_fn.functionName && function.equals(other_fn.function)
+ && arguments.equals(other_fn.arguments)) {
+ is_same = true;
+
+ } else {
+ is_same = false;
+ }
+ }
+ return is_same;
+ }
+
+ public boolean equals(FunctionCall fn) {
+ return sameAs(fn);
+ }
+
+ public static HashMap<String, ClassAdFunc> getFunctionTable() {
+ return funcTable;
+ }
+
+ public static synchronized void registerFunction(String functionName, ClassAdFunc function) {
+ if (!funcTable.containsKey(functionName)) {
+ funcTable.put(functionName, function);
+ }
+ }
+
+ @Override
+ public void privateSetParentScope(ClassAd parent) {
+ arguments.privateSetParentScope(parent);
+ }
+
+ //This will move pointers to objects (not create clones)
+ public void getComponents(AMutableString fn, ExprList exprList) {
+ fn.setValue(functionName);
+ for (ExprTree tree : arguments.getExprList()) {
+ exprList.add(tree);
+ }
+ }
+
+ public void getComponents(AMutableCharArrayString fn, ExprList exprList) {
+ fn.setValue(functionName);
+ for (ExprTree tree : arguments.getExprList()) {
+ exprList.add(tree);
+ }
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value value) throws HyracksDataException {
+ if (function != null) {
+ return function.call(functionName, arguments, state, value);
+ } else {
+ value.setErrorValue();
+ return (true);
+ }
+ }
+
+ @Override
+ public boolean privateEvaluate(EvalState state, Value value, ExprTreeHolder tree) throws HyracksDataException {
+ FunctionCall tmpSig = new FunctionCall();
+ Value tmpVal = new Value();
+ ExprTreeHolder argSig = new ExprTreeHolder();
+ MutableBoolean rval = new MutableBoolean();
+ if (!privateEvaluate(state, value)) {
+ return false;
+ }
+ tmpSig.functionName = functionName;
+ rval.setValue(true);
+ for (ExprTree i : arguments.getExprList()) {
+ rval.setValue(i.publicEvaluate(state, tmpVal, argSig));
+ if (rval.booleanValue())
+ tmpSig.arguments.add(argSig.getInnerTree());
+ }
+ tree.setInnerTree(tmpSig);
+ return rval.booleanValue();
+ }
+
+ @Override
+ public boolean privateFlatten(EvalState state, Value value, ExprTreeHolder tree, AMutableInt32 i)
+ throws HyracksDataException {
+ FunctionCall newCall = new FunctionCall();
+ ExprTreeHolder argTree = new ExprTreeHolder();
+ Value argValue = new Value();
+ boolean fold = true;
+
+ tree.setInnerTree(null); // Just to be safe... wenger 2003-12-11.
+
+ // if the function cannot be resolved, the value is "error"
+ if (function == null) {
+ value.setErrorValue();
+ return true;
+ }
+
+ newCall.functionName = functionName;
+ newCall.function = function;
+
+ // flatten the arguments
+ for (ExprTree exp : arguments.getExprList()) {
+ if (exp.publicFlatten(state, argValue, argTree)) {
+ if (argTree.getInnerTree() != null) {
+ newCall.arguments.add(argTree.getInnerTree());
+ fold = false;
+ continue;
+ } else {
+ // Assert: argTree == NULL
+ argTree.setInnerTree(Literal.createLiteral(argValue));
+ if (argTree.getInnerTree() != null) {
+ newCall.arguments.add(argTree.getInnerTree());
+ continue;
+ }
+ }
+ }
+
+ // we get here only when something bad happens
+ value.setErrorValue();
+ tree.setInnerTree(null);
+ return false;
+ }
+
+ // assume all functions are "pure" (i.e., side-affect free)
+ if (fold) {
+ // flattened to a value
+ if (!function.call(functionName, arguments, state, value)) {
+ return false;
+ }
+ tree.setInnerTree(null);
+ } else {
+ tree.setInnerTree(newCall);
+ }
+ return true;
+ }
+
+ @Override
+ public NodeKind getKind() {
+ return NodeKind.FN_CALL_NODE;
+ }
+
+ @Override
+ public void reset() {
+ this.arguments.clear();
+ this.function = null;
+ this.functionName = "";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/ac683db0/asterix-external-data/src/test/java/org/apache/asterix/external/classad/InputStreamLexerSource.java
----------------------------------------------------------------------
diff --git a/asterix-external-data/src/test/java/org/apache/asterix/external/classad/InputStreamLexerSource.java b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/InputStreamLexerSource.java
new file mode 100644
index 0000000..38ab591
--- /dev/null
+++ b/asterix-external-data/src/test/java/org/apache/asterix/external/classad/InputStreamLexerSource.java
@@ -0,0 +1,111 @@
+/*
+ * 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.asterix.external.classad;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class InputStreamLexerSource extends LexerSource {
+ // This source allows input from a stream. Note that
+ // the user passes in a pointer to the stream.
+
+ public int position = 0;
+ public char[] buffer = new char[512];
+ private BufferedReader reader;
+ public int validBytes;
+
+ public InputStreamLexerSource(InputStream in) {
+ this.reader = new BufferedReader(new InputStreamReader(in));
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ public InputStreamLexerSource() {
+ }
+
+ public InputStreamLexerSource(BufferedReader reader) {
+ this.reader = reader;
+ }
+
+ @Override
+ public char readCharacter() throws IOException {
+ if (position < validBytes) {
+ previousCharacter = buffer[position];
+ position++;
+ return previousCharacter;
+ } else {
+ fillBuffer();
+ }
+ if (position < validBytes) {
+ previousCharacter = buffer[position];
+ position++;
+ return previousCharacter;
+ }
+ return '\0';
+ }
+
+ private void fillBuffer() throws IOException {
+ position = 0;
+ // we leave an empty location at the end to take care of corner case of unread
+ validBytes = reader.read(buffer, 0, buffer.length - 1);
+ }
+
+ @Override
+ public void unreadCharacter() {
+ if (position == 0) {
+ System.arraycopy(buffer, 0, buffer, 1, buffer.length - 1);
+ buffer[0] = previousCharacter;
+ validBytes++;
+ return;
+ } else {
+ position--;
+ }
+ }
+
+ @Override
+ public boolean atEnd() throws IOException {
+ if (position < validBytes) {
+ return false;
+ }
+ fillBuffer();
+ return position == validBytes;
+ }
+
+ public void setNewSource(InputStream stream) {
+ this.reader = new BufferedReader(new InputStreamReader(stream));
+ this.position = 0;
+ this.validBytes = 0;
+ }
+
+ public void setNewSource(BufferedReader reader) {
+ this.reader = reader;
+ this.position = 0;
+ this.validBytes = 0;
+ }
+
+ @Override
+ public char[] getBuffer() {
+ return buffer;
+ }
+}
|