asterixdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From amo...@apache.org
Subject [13/34] incubator-asterixdb git commit: Enabled Feed Tests and Added External Library tests
Date Mon, 22 Feb 2016 22:35:01 GMT
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;
+    }
+}


Mime
View raw message