You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by wi...@apache.org on 2013/02/19 13:57:40 UTC

[5/11] added missing module from the previous code importation

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/main/javacc/at/newmedialab/ldpath/parser/rdfpath.jj
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/main/javacc/at/newmedialab/ldpath/parser/rdfpath.jj b/ldpath/ldpath-core/src/main/javacc/at/newmedialab/ldpath/parser/rdfpath.jj
new file mode 100644
index 0000000..4e45588
--- /dev/null
+++ b/ldpath/ldpath-core/src/main/javacc/at/newmedialab/ldpath/parser/rdfpath.jj
@@ -0,0 +1,1012 @@
+options
+{
+  STATIC=false;
+//  LOOKAHEAD=5;
+  CACHE_TOKENS=true;
+//  FORCE_LA_CHECK=true;
+//  CHOICE_AMBIGUITY_CHECK=5;
+  LOOKAHEAD=2147483647;
+//  DEBUG_PARSER=true;
+//  DEBUG_TOKEN_MANAGER=true;
+//  DEBUG_LOOKAHEAD=true;
+}
+
+PARSER_BEGIN(RdfPathParser)
+package at.newmedialab.ldpath.parser;
+
+import at.newmedialab.ldpath.model.Constants;
+
+import at.newmedialab.ldpath.api.backend.*;
+import at.newmedialab.ldpath.api.functions.*;
+import at.newmedialab.ldpath.api.selectors.*;
+import at.newmedialab.ldpath.api.tests.*;
+import at.newmedialab.ldpath.api.transformers.*;
+
+
+import at.newmedialab.ldpath.model.fields.*;
+import at.newmedialab.ldpath.model.functions.*;
+import at.newmedialab.ldpath.model.programs.*;
+import at.newmedialab.ldpath.model.selectors.*;
+import at.newmedialab.ldpath.model.tests.*;
+import at.newmedialab.ldpath.model.transformers.*;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Collections;
+
+import java.io.Reader;
+import java.io.InputStream;
+
+import java.net.URI;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+@SuppressWarnings("all")
+public class RdfPathParser<Node> {
+
+        private enum Mode { RULE, SELECTOR, TEST, PROGRAM, PREFIX };
+
+
+        private static final Logger log = LoggerFactory.getLogger(RdfPathParser.class);
+
+        /**
+         * A map mapping from namespace prefix to namespace URI
+         */
+        private Map<String,String> namespaces = new HashMap<String,String>();
+
+        private RDFBackend<Node> backend;
+
+        private Configuration config;
+
+        private Mode mode = Mode.PROGRAM;
+
+        public RdfPathParser(RDFBackend<Node> backend, Reader in) {
+            this(backend,null,in);
+        }
+
+        public RdfPathParser(RDFBackend<Node> backend, Configuration config, Reader in) {
+            this(in);
+            this.backend = backend;
+            if(config == null) {
+                this.config = new DefaultConfiguration();
+            } else {
+                this.config = config;
+            }
+
+            initialise();
+        }
+
+        public RdfPathParser(RDFBackend<Node> backend, InputStream in) {
+            this(backend,null,in);
+        }
+
+        public RdfPathParser(RDFBackend<Node> backend, Configuration config, InputStream in) {
+            this(in);
+            this.backend = backend;
+            if(config == null) {
+                this.config = new DefaultConfiguration();
+            } else {
+                this.config = config;
+            }
+
+            initialise();
+        }
+
+        public RdfPathParser(RDFBackend<Node> backend, InputStream in, String encoding) {
+            this(backend,null,in,encoding);
+        }
+
+        public RdfPathParser(RDFBackend<Node> backend, Configuration config, InputStream in, String encoding) {
+            this(in,encoding);
+            this.backend = backend;
+            if(config == null) {
+                this.config = new DefaultConfiguration();
+            } else {
+                this.config = config;
+            }
+
+            initialise();
+        }
+
+        public Program<Node> parseProgram() throws ParseException {
+			namespaces.clear();
+			namespaces.putAll(config.getNamespaces());
+
+            mode = Mode.PROGRAM;
+            try {
+		        return Program();
+		    } catch(TokenMgrError error){
+		        throw new ParseException("Unable to parse Program: (Message: "+error.getMessage()+")");
+		    }
+        }
+
+        public Entry<String, String> parsePrefix() throws ParseException {
+            namespaces.clear();
+            namespaces.putAll(config.getNamespaces());
+            mode = Mode.PREFIX;
+            try {
+                return Namespace();
+            } catch (TokenMgrError error) {
+                throw new ParseException("Unable to parse Prefix: (Message: "+ error.getMessage()+")");
+            }
+        }
+
+        public Map<String, String> parsePrefixes() throws ParseException {
+            namespaces.clear();
+            namespaces.putAll(config.getNamespaces());
+            mode = Mode.PREFIX;
+            try {
+                return Namespaces();
+            } catch (TokenMgrError error) {
+                throw new ParseException("Unable to parse Prefixes: (Message: "+ error.getMessage()+")");
+            }
+        }
+
+
+        public NodeSelector<Node> parseSelector(Map<String,String> ctxNamespaces) throws ParseException {
+			namespaces.clear();
+			namespaces.putAll(config.getNamespaces());
+			if(ctxNamespaces != null) {
+			    namespaces.putAll(ctxNamespaces);
+			}
+
+			mode = Mode.SELECTOR;
+
+            try {
+    		    return Selector();
+            } catch(TokenMgrError error){
+                throw new ParseException("Unable to parse Selector: (Message: "+error.getMessage()+")");
+            }
+        }
+        
+        public NodeTest<Node> parseTest(Map<String, String> ctxNamespaces) throws ParseException {
+            namespaces.clear();
+            namespaces.putAll(config.getNamespaces());
+            if (ctxNamespaces != null) {
+                namespaces.putAll(ctxNamespaces);
+            }
+            mode = Mode.TEST;
+            try {
+                return NodeTest();
+            } catch (TokenMgrError error) {
+                throw new ParseException("Unable to parse Test: (Message: "+ error.getMessage()+")");
+            }
+        }
+
+        public <T> FieldMapping<T,Node> parseRule(Map<String,String> ctxNamespaces) throws ParseException {
+			namespaces.clear();
+			namespaces.putAll(config.getNamespaces());
+			if(ctxNamespaces != null) {
+			    namespaces.putAll(ctxNamespaces);
+			}
+
+			mode = Mode.RULE;
+
+            try {
+	            return Rule();
+            } catch(TokenMgrError error){
+                throw new ParseException("Unable to parse Rule: (Message: "+error.getMessage()+")");
+            }
+        }
+
+
+        public Node resolveResource(String uri) {
+            return backend.createURI(uri);
+        }
+
+
+        public String resolveNamespace(String prefix) throws ParseException {
+            String uri = namespaces.get(prefix);
+            if(uri == null) {
+                throw new ParseException("Namespace "+prefix+" not defined!");
+            }
+            return uri;
+        }
+
+
+        public SelectorFunction<Node> getFunction(String uri) throws ParseException {
+            if(xsdNodeFunctionMap.get(uri) != null) {
+               return xsdNodeFunctionMap.get(uri);
+            } else {
+                throw new ParseException("function with URI "+uri+" does not exist");
+            }
+        }
+
+        public TestFunction<Node> getTestFunction(String uri) throws ParseException {
+            if (xsdNodeTestMap.get(uri) != null) {
+                return xsdNodeTestMap.get(uri);
+            } else {
+                throw new ParseException("test function with URI "+uri+" does not exist");
+            }
+        }
+
+        public NodeTransformer<?,Node> getTransformer(String uri) throws ParseException {
+            if(xsdNodeTransformerMap.get(uri) != null) {
+                return xsdNodeTransformerMap.get(uri);
+            } else {
+                throw new ParseException("transformer with URI "+uri+" does not exist");
+            }
+        }
+
+
+        private void initialise() {
+            initTransformerMappings();
+            initFunctionMappings();
+        }
+
+        /**
+         * Register the function passed as argument in this parser's function map.
+         */
+        public void registerFunction(SelectorFunction<Node> function) {
+            registerFunction(xsdNodeFunctionMap,function);
+        }
+
+    public void registerFunction(TestFunction<Node> test) {
+        registerTest(xsdNodeTestMap, test);
+    }
+
+        /**
+         * Register the result transformer passed as argument for the given type uri.
+         */
+        public void registerTransformer(String typeUri, NodeTransformer<?,Node> transformer) {
+            xsdNodeTransformerMap.put(typeUri,transformer);
+        }
+
+
+        /**
+         * A map mapping from XSD types to node transformers.
+         */
+        private Map<String, NodeTransformer<?,Node>> xsdNodeTransformerMap;
+        private void initTransformerMappings() {
+            Map<String, NodeTransformer<?,Node>> transformerMap = new HashMap<String, NodeTransformer<?,Node>>();
+
+            transformerMap.putAll(config.getTransformers());
+
+            xsdNodeTransformerMap = transformerMap;
+        }
+
+
+        private Map<String, SelectorFunction<Node>> xsdNodeFunctionMap;
+    private Map<String, TestFunction<Node>> xsdNodeTestMap;
+        private void initFunctionMappings() {
+            Map<String, SelectorFunction<Node>> functionMap = new HashMap<String, SelectorFunction<Node>>();
+
+            functionMap.putAll(config.getFunctions());
+
+            xsdNodeFunctionMap = functionMap;
+
+        Map<String, TestFunction<Node>> testMap = new HashMap<String, TestFunction<Node>>();
+        testMap.putAll(config.getTestFunctions());
+        xsdNodeTestMap = testMap;
+        }
+
+        private void registerFunction(Map<String, SelectorFunction<Node>> register, final SelectorFunction<Node> function) {
+            register.put(Constants.NS_LMF_FUNCS + function.getPathExpression(backend), function);
+        }
+        
+    private void registerTest(Map<String, TestFunction<Node>> register, final TestFunction<Node> test) {
+        register.put(Constants.NS_LMF_FUNCS + test.getLocalName(), test);
+    }
+
+        private class Namespace implements Entry<String, String> {
+            private String key, val;
+            public Namespace(String key, String val) {
+                this.key = key;
+                this.val = val;
+            }
+            @Override
+            public String getKey() {
+                return key;
+            }
+            @Override
+            public String getValue() {
+                return val;
+            }
+            @Override
+            public String setValue(String value) {
+                String oV = val;
+                val = value;
+                return oV;
+            }
+        }
+
+}
+PARSER_END(RdfPathParser)
+
+SKIP :
+{
+ 	" "
+|	"\r"
+|	"\t"
+|	"\n"
+}
+
+MORE:
+{
+ "\"" : WithinString
+}
+
+<WithinString> TOKEN:
+{
+  <STRLIT: "\""> : DEFAULT
+}
+
+<WithinString> MORE:
+{
+  <~["\n","\r"]>
+}
+
+
+TOKEN : /* OPERATORS */
+{
+	< AND:  "&" >    |
+	< OR:   "|" >    |
+	< NOT:  "!" >    |
+	< IS:   "is" >   |
+	< IS_A: "is-a" > |
+  	< TYPE: "^^" >   |
+  	< LANG: "@" >
+}
+
+TOKEN :
+{
+    < URI: ["a"-"z","A"-"Z"](["a"-"z","A"-"Z","0"-"9","+","-","."])* "://"  (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+ ("#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])*)? | "#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+> |
+    < IDENTIFIER: ["a"-"z","A"-"Z","0"-"9","_"](["a"-"z","A"-"Z","0"-"9","_","'","-"])* > |
+    < #URICHAR: ["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"] >
+}
+
+Map<String, String> Namespaces() :
+{
+    HashMap<String, String> ns = new HashMap<String, String>();
+    Entry<String, String> namespace = null;
+}
+{
+  (
+     namespace = Namespace() {
+       ns.put(namespace.getKey(), namespace.getValue());
+     }
+  )+
+  {
+    return ns;
+  }   
+}
+
+Entry<String, String> Namespace() :
+{
+    Token id = null;
+    Token uri;
+}
+{
+  ( 
+    "@prefix" id = <IDENTIFIER> ":" "<" uri = <URI> ">" (";")? {
+    }
+  ) { return new Namespace(id.image, uri.image); }
+}
+
+Program Program() :
+{
+    Program<Node> program = new Program();
+    NodeTest<Node> filter = null;
+    Map<String, String> nss = null;
+    FieldMapping<?,Node> rule;
+    NodeSelector<Node> boostSelector;
+}
+{
+  (
+    nss = Namespaces() {
+        namespaces.putAll(nss);
+        for (String k : nss.keySet()) {
+            program.addNamespace(k, nss.get(k));
+        }
+    }
+  )?
+
+  (
+    "@filter" filter = NodeTest() ";" {
+        program.setFilter(filter);
+    }
+  )?
+
+  (
+    "@boost" boostSelector = Selector() ";" {
+    	NodeTransformer transformer = getTransformer(Program.DOCUMENT_BOOST_TYPE);
+		FieldMapping booster = new FieldMapping("@boost", Program.DOCUMENT_BOOST_TYPE, boostSelector, transformer, null);
+		program.setBooster(booster);  
+    }
+  )?
+
+  (
+    rule = Rule()
+    {
+       program.addMapping(rule);
+    }
+  )*
+  <EOF>
+  {
+    return program;
+  }
+}
+
+FieldMapping Rule() :
+{
+    FieldMapping<?,Node> rule;
+    Token name;
+    String uri;
+    String type = null;
+    NodeSelector<Node> selector;
+    NodeTransformer<?,Node> transformer;
+    Map<String, String> conf = null;
+}
+{
+    name = <IDENTIFIER> "=" selector = Selector() ("::" type = Uri())? ("(" conf = FieldConfig() ")")? ";" {
+        if(type != null) {
+            transformer = getTransformer(type);
+        } else {
+            transformer = new IdentityTransformer();
+        }
+        if(mode != Mode.PROGRAM) {
+            throw new ParseException("cannot use field names when parsing single paths");
+        }
+        rule = new FieldMapping(name.image,type,selector,transformer, conf);
+        return rule;
+    }
+|   uri = Uri() "=" selector = Selector() ("::" type = Uri())? ("(" conf = FieldConfig() ")")? ";" {
+        if(type != null) {
+            transformer = getTransformer(type);
+        } else {
+            transformer = new IdentityTransformer();
+        }
+        if(mode != Mode.PROGRAM) {
+            throw new ParseException("cannot use field names when parsing single paths");
+        }
+        rule = new FieldMapping(uri,type,selector,transformer, conf);
+        return rule;
+    }
+|   selector = Selector() ("::" type = Uri())? ("(" conf = FieldConfig() ")")? (";")? {
+        if(type != null) {
+            transformer = getTransformer(type);
+        } else {
+            transformer = new IdentityTransformer();
+        }
+        if(mode != Mode.PROGRAM && conf != null) {
+            throw new ParseException("cannot use configuration parameters when parsing single paths");
+        }
+        try {
+            rule = new FieldMapping(selector.getName(backend),type,selector,transformer, conf);
+        } catch(UnsupportedOperationException ex) {
+            if(mode == Mode.PROGRAM) {
+                rule = new FieldMapping("unnamed",type,selector,transformer, conf);
+                log.error("error while parsing {}: {}", rule.getPathExpression(backend),ex.getMessage());
+                throw new ParseException("error while parsing "+rule.getPathExpression(backend)+": "+ex.getMessage());
+            } else {
+                rule = new FieldMapping("unnamed",type,selector,transformer, conf);
+            }
+        }
+        return rule;
+    }
+}
+
+
+Map<String,String> FieldConfig() : {
+	Map<String, String> conf = new HashMap<String, String>();
+	Token key = null;
+	String val = null;
+	Map<String,String> more = null;
+}
+{
+	( key = <IDENTIFIER> "=" val = ConfVal() ("," more = FieldConfig() )? )? {
+		if (key == null || val == null) return null;
+		conf.put(key.image, val);
+		if (more != null) {
+			conf.putAll(more);
+		}
+		return conf;
+	}
+}
+
+String ConfVal() : {
+	Token str, id;
+}
+{
+	str = <STRLIT> { return str.image.substring(1, str.image.length() -1); }
+|	id = <IDENTIFIER> { return id.image; }
+}
+
+String Uri() : {
+    Token uri, prefix, localName;
+
+}
+{
+    "<" uri = <URI> ">" {
+       return uri.image;
+    }
+|   prefix = <IDENTIFIER> ":" localName = <IDENTIFIER> {
+        return resolveNamespace(prefix.image)+localName.image;
+    }
+}
+
+
+NodeSelector Selector() :
+{
+    NodeSelector result;
+}
+{
+    (
+        result = CompoundSelector()
+    |   result = TestingSelector()
+    |   result = AtomicSelector()
+    )
+    {
+        return result;
+    }
+}
+
+
+NodeSelector CompoundSelector() :
+{
+    NodeSelector result = null;
+}
+{
+    (
+        /* Union Selector */
+        result = UnionSelector() |
+
+        /* Intersection Selector */
+        result = IntersectionSelector() |
+
+        /* Path Selector */
+        result = PathSelector()
+
+
+    )
+    {
+        return result;
+    }
+}
+
+/**
+ * As path elements, we do not allow arbitrary compound selectors, but we allow all atomic and path selectors.
+ */
+NodeSelector AtomicOrTestingOrPathSelector() :
+{
+    NodeSelector result = null;
+}
+{
+    (
+        /* Path Selector */
+        result = PathSelector() |
+
+        /* Atomic Selector */
+        result = AtomicOrTestingSelector()
+    )
+    {
+        return result;
+    }
+}
+
+NodeSelector AtomicOrTestingSelector() :
+{
+    NodeSelector result = null;
+}
+{
+    (
+        /* Testing Selector */
+        result = TestingSelector() |
+
+        /* Atomic Selector */
+        result = AtomicSelector()
+    )
+    {
+        return result;
+    }
+}
+
+NodeSelector AtomicSelector() :
+{
+    NodeSelector result = null;
+}
+{
+    (
+    	/* Self Selector */
+    	result = SelfSelector() |
+    	    
+        /* Property Selector */
+        result = PropertySelector() |
+
+        /* Wildcard Selector */
+        result = WildcardSelector() |
+        
+        /* Reverse Property Selector */
+        result = ReversePropertySelector() |
+
+        /* Function Selector */
+        result = FunctionSelector() |
+
+        /* String Constant Selector */
+        result = StringConstantSelector() |
+        
+        /* Recursive Path Selector */
+        result = RecursivePathSelector() |
+
+        /* Other selector enclosed in braces */
+        result = GroupedSelector()
+
+    )
+    {
+        return result;
+    }
+}
+
+NodeSelector SelfSelector() :
+{
+}
+{
+	"." { return new SelfSelector(); }
+}
+
+NodeSelector GroupedSelector() :
+{
+    NodeSelector result = null;
+}
+{
+    /* Other selector enclosed in braces */
+    "(" result = Selector() ")"
+    {
+        return result;
+    }
+
+}
+
+RecursivePathSelector RecursivePathSelector() :
+{
+	RecursivePathSelector result = null;
+	NodeSelector delegate        = null;
+}
+{
+	"(" delegate = Selector() ")" "+"
+	{
+		result = RecursivePathSelector.getPathSelectorPlused(delegate);
+		return result;
+	} |
+    "(" delegate = Selector() ")" "*"
+    {
+        result = RecursivePathSelector.getPathSelectorStared(delegate);
+        return result;
+    } 
+}
+
+PathSelector PathSelector() :
+{
+    PathSelector result = null;
+    NodeSelector left   = null;
+    NodeSelector right  = null;
+}
+{
+    left = AtomicOrTestingSelector() "/" right = AtomicOrTestingOrPathSelector()
+    {
+        result = new PathSelector(left,right);
+        return result;
+    }
+}
+
+IntersectionSelector IntersectionSelector() :
+{
+    IntersectionSelector result = null;
+    NodeSelector left   = null;
+    NodeSelector right  = null;
+}
+{
+    left = AtomicOrTestingOrPathSelector() "&" right = Selector()
+    {
+        result = new IntersectionSelector(left,right);
+        return result;
+    }
+}
+
+UnionSelector UnionSelector() :
+{
+    UnionSelector result = null;
+    NodeSelector left   = null;
+    NodeSelector right  = null;
+}
+{
+    left = AtomicOrTestingOrPathSelector() "|" right = Selector()
+    {
+        result = new UnionSelector(left,right);
+        return result;
+    }
+}
+
+TestingSelector TestingSelector() :
+{
+    TestingSelector result = null;
+    NodeSelector delegate  = null;
+    NodeTest test = null;
+}
+{
+    delegate = AtomicSelector() "[" test = NodeTest() "]" {
+        result = new TestingSelector(delegate,test);
+        return result;
+    }
+}
+
+ReversePropertySelector ReversePropertySelector() :
+{
+	ReversePropertySelector result = null;
+	Node property;
+    String uri;
+}
+{
+	"^" uri = Uri() {
+        property = resolveResource(uri);
+        result   = new ReversePropertySelector(property);
+        return result;
+	}
+}
+
+PropertySelector PropertySelector() :
+{
+    PropertySelector result = null;
+    Node property;
+    String uri;
+}
+{
+    uri = Uri() {
+        property = resolveResource(uri);
+        result   = new PropertySelector(property);
+        return result;
+    }
+}
+
+WildcardSelector WildcardSelector() :
+{
+    WildcardSelector result = null;
+}
+{
+    "*" {
+        result = new WildcardSelector();
+        return result;
+    }
+}
+
+FunctionSelector FunctionSelector() :
+{
+    FunctionSelector result = null;
+    List<NodeSelector> arguments = new ArrayList<NodeSelector>();
+    NodeSelector argument;
+    String uri;
+    Token fName;
+}
+{
+    /* Function-Calls without arguments can skip parenthesis */
+    /* Does not work... why?
+    "fn:" fName = <IDENTIFIER> {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionSelector(getFunction(uri),Collections.emptyList());
+           return result;
+    } | */
+    /* Functions do not need to have arguments */
+    "fn:" fName = <IDENTIFIER> "(" ")" {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionSelector(getFunction(uri),Collections.emptyList());
+           return result;
+    } |
+    /* Sometimes arguments are required */
+    "fn:" fName = <IDENTIFIER> "("
+            argument = Selector() { arguments.add(argument); }
+            ( "," argument = Selector() { arguments.add(argument); } )*
+    ")" {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionSelector(getFunction(uri),arguments);
+           return result;
+    }
+}
+
+
+StringConstantSelector StringConstantSelector() :
+{
+    StringConstantSelector result = null;
+    Token literal;
+}
+{
+    literal = <STRLIT> {
+        result = new StringConstantSelector(literal.image.substring(1, literal.image.length()-1));
+        return result;
+    }
+}
+
+
+
+NodeTest NodeTest() :
+{
+    NodeTest result;
+}
+{
+    (
+        result = GroupedTest()
+    |   result = NotTest()
+    |   result = AndTest()
+    |   result = OrTest()
+    |   result = AtomicNodeTest()
+    )
+    {
+        return result;
+    }
+}
+
+NodeTest GroupedTest() :
+{
+    NodeTest delegate;
+}
+{
+    "(" delegate = NodeTest() ")" {
+       return delegate;
+    }
+}
+
+NodeTest AtomicNodeTest() :
+{
+    NodeTest result;
+}
+{
+    (
+        result = LiteralLanguageTest()
+    |   result = LiteralTypeTest()
+    |   result = TypeTest()
+    |   result = PathEqualityTest()
+    |   result = FunctionTest()
+    |   result = PathTest()
+    )
+    {
+        return result;
+    }
+}
+
+FunctionTest FunctionTest() :
+{
    FunctionTest result;
+    List<NodeSelector> arguments = new ArrayList<NodeSelector>();
+    NodeSelector argument;
+    String uri;
+    Token fName;
+}
+{
+  (    
+    /* Function-Calls without arguments can skip parenthesis */
+    /* Does not work... why?
+    "fn:" fName = <IDENTIFIER> {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionSelector(getTestFunction(uri),Collections.emptyList());
+    } | */
+    /* Functions do not need to have arguments */
+    "fn:" fName = <IDENTIFIER> "(" ")" {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionTest(getTestFunction(uri),Collections.emptyList());
+    } |
+    /* Sometimes arguments are required */
+    "fn:" fName = <IDENTIFIER> "("
+            argument = Selector() { arguments.add(argument); }
+            ( "," argument = Selector() { arguments.add(argument); } )*
+    ")" {
+           uri = namespaces.get("fn") + fName.image;
+           result = new FunctionTest(getTestFunction(uri),arguments);
+    }
+  )
+  {
    return result;
  }
}
+
+LiteralLanguageTest LiteralLanguageTest():
+{
+    Token lang;
+}
+{
+    "@" lang = <IDENTIFIER> {
+        return new LiteralLanguageTest(lang.image);
+    }
+}
+
+LiteralTypeTest LiteralTypeTest():
+{
+    String type;
+}
+{
+    "^^" type = Uri() {
+        return new LiteralTypeTest(type);
+    }
+}
+
+NotTest NotTest() :
+{
+    NodeTest delegate;
+}
+{
+    "!" delegate = NodeTest() {
+        return new NotTest(delegate);
+    }
+}
+
+PathEqualityTest TypeTest() :
+{
+    Node node;
+}
+{
+    <IS_A> node = Node() {
+        return new PathEqualityTest(new PropertySelector(resolveResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")), node);
+    }
+}
+
+
+AndTest AndTest():
+{
+    NodeTest left, right;
+}
+{
+    left = AtomicNodeTest() "&" right = NodeTest() {
+        return new AndTest(left,right);
+    }
+}
+
+OrTest OrTest():
+{
+    NodeTest left, right;
+}
+{
+    left = AtomicNodeTest() "|" right = NodeTest() {
+        return new OrTest(left,right);
+    }
+}
+
+PathEqualityTest PathEqualityTest():
+{
+    NodeSelector path;
+    Node node;
+}
+{
+    path = Selector() "is" node = Node() {
+        return new PathEqualityTest(path,node);
+    }
+}
+
+
+Node Node():
+{
+    String uri, type = null;
+    Token literal, language;
+}
+{
+    uri = Uri() {
+        return resolveResource(uri);
+    }
+|   literal = <STRLIT>  ("^^" type = Uri() )? {
+        try {
+            return backend.createLiteral(literal.image.substring(1, literal.image.length()-1),null,type == null ? null : new URI(type));
+        } catch(java.net.URISyntaxException ex) {
+            throw new ParseException("could not parse type URI "+type+": "+ex.getMessage());
+	    }
+    }
+|   literal = <STRLIT>  "@" language = <IDENTIFIER> {
+        return backend.createLiteral(literal.image.substring(1, literal.image.length()-1),new Locale(language.image),null);
+    }
+}
+
+
+PathTest PathTest():
+{
+    NodeSelector path;
+}
+{
+    (
+        path = PathSelector()
+    |   path = TestingSelector()
+    |   path = AtomicSelector()
+    )
+    {
+        return new PathTest(path);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/main/resources/META-INF/MANIFEST.MF
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/main/resources/META-INF/MANIFEST.MF b/ldpath/ldpath-core/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..07a1274
--- /dev/null
+++ b/ldpath/ldpath-core/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+SPI-Consumer: *

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction b/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
new file mode 100644
index 0000000..6fac049
--- /dev/null
+++ b/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
@@ -0,0 +1,5 @@
+at.newmedialab.ldpath.model.functions.ConcatenateFunction
+at.newmedialab.ldpath.model.functions.FirstFunction
+at.newmedialab.ldpath.model.functions.LastFunction
+at.newmedialab.ldpath.model.functions.SortFunction
+at.newmedialab.ldpath.model.functions.CountFunction
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.TestFunction
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.TestFunction b/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.TestFunction
new file mode 100644
index 0000000..99a27a3
--- /dev/null
+++ b/ldpath/ldpath-core/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.TestFunction
@@ -0,0 +1,6 @@
+at.newmedialab.ldpath.model.tests.functions.EqualTest
+at.newmedialab.ldpath.model.tests.functions.NotEqualTest
+at.newmedialab.ldpath.model.tests.functions.GreaterEqualTest
+at.newmedialab.ldpath.model.tests.functions.GreaterThanTest
+at.newmedialab.ldpath.model.tests.functions.LessEqualTest
+at.newmedialab.ldpath.model.tests.functions.LessThanTest
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/java/core/AbstractTestBase.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/java/core/AbstractTestBase.java b/ldpath/ldpath-core/src/test/java/core/AbstractTestBase.java
new file mode 100644
index 0000000..43dd7db
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/java/core/AbstractTestBase.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 core;
+
+import at.newmedialab.ldpath.api.selectors.NodeSelector;
+import at.newmedialab.ldpath.backend.sesame.SesameRepositoryBackend;
+import at.newmedialab.ldpath.model.fields.FieldMapping;
+import at.newmedialab.ldpath.parser.ParseException;
+import at.newmedialab.ldpath.parser.RdfPathParser;
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.repository.sail.SailRepositoryConnection;
+import org.openrdf.rio.RDFFormat;
+import org.openrdf.rio.RDFParseException;
+import org.openrdf.sail.memory.MemoryStore;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeThat;
+
+public abstract class AbstractTestBase {
+    protected final static Map<String, String> NSS;
+    static {
+        Map<String, String> nss = new HashMap<String, String>();
+
+        nss.put("ex", "http://www.example.com/");
+        nss.put("foo", "http://localhost/vcab#");
+
+        NSS = Collections.unmodifiableMap(nss);
+    }
+
+    protected static String ns(String prefix) {
+        final String ns = NSS.get(prefix);
+        if (ns == null) { throw new NoSuchElementException("Unknown Prefix '" + prefix + "'"); }
+        return ns;
+    }
+
+    protected static String ns(String prefix, String local) {
+        return ns(prefix) + local;
+    }
+
+    protected SesameRepositoryBackend backend;
+    protected SailRepository repository;
+
+    @Before
+    public void before() throws RepositoryException {
+
+        repository = new SailRepository(new MemoryStore());
+        repository.initialize();
+
+        backend = new SesameRepositoryBackend(repository);
+
+    }
+
+    @After
+    public void after() throws RepositoryException {
+        repository.shutDown();
+    }
+
+    protected RdfPathParser<Value> createParserFromResource(String input) throws IOException {
+        final URL resource = this.getClass().getResource(input);
+        assumeThat("Could not load test input data '" + input + "'", resource, CoreMatchers.notNullValue());
+
+        RdfPathParser<Value> parser = new RdfPathParser<Value>(backend, resource.openStream());
+        assertThat("Could not parse ldPath", parser, CoreMatchers.notNullValue());
+
+        return parser;
+    }
+
+    protected RdfPathParser<Value> createParserFromString(String program) {
+        final RdfPathParser<Value> parser = new RdfPathParser<Value>(backend, new StringReader(program));
+        assertThat("Could not parse ldPath", parser, CoreMatchers.notNullValue());
+
+        return parser;
+    }
+
+    protected final void loadData(String datafile, RDFFormat format) throws RepositoryException, RDFParseException, IOException {
+        loadData(datafile, format, "http://www.example.com/ldpath/");
+    }
+
+    protected final void loadData(String datafile, RDFFormat format, String baseURI) throws RepositoryException, RDFParseException, IOException {
+        // load demo data
+        InputStream data = this.getClass().getResourceAsStream(datafile);
+        Assume.assumeThat("Could not load test-data: " + datafile, data, notNullValue(InputStream.class));
+
+        final SailRepositoryConnection con = repository.getConnection();
+        try {
+
+            con.add(data, baseURI, format);
+
+            con.commit();
+        } finally {
+            con.close();
+        }
+    }
+
+    protected Collection<Object> evaluateRule(final String ldPath, URI context) throws ParseException {
+        final RdfPathParser<Value> parser = createParserFromString(ldPath);
+        final FieldMapping<Object, Value> rule = parser.parseRule(NSS);
+        final Collection<Object> values = rule.getValues(backend, context);
+        return values;
+    }
+
+    protected URI createURI(String uri) {
+        if (uri.matches("\\w+:[a-zA-z0-9_%-]+")) {
+            final String[] s = uri.split(":");
+            return repository.getValueFactory().createURI(ns(s[0], s[1]));
+        } else {
+            return repository.getValueFactory().createURI(uri);
+        }
+    }
+
+    protected URI createURI(String prefix, String local) {
+        return repository.getValueFactory().createURI(ns(prefix, local));
+    }
+
+    protected Collection<Value> evaluateSelector(final String ldPath, URI context) throws ParseException {
+        final RdfPathParser<Value> parser = createParserFromString(ldPath);
+        final NodeSelector<Value> sel = parser.parseSelector(NSS);
+        final Collection<Value> nodes = sel.select(backend, context, null, null);
+        return nodes;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/java/ldpath/BinaryNumericTestFunctionsTest.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/java/ldpath/BinaryNumericTestFunctionsTest.java b/ldpath/ldpath-core/src/test/java/ldpath/BinaryNumericTestFunctionsTest.java
new file mode 100644
index 0000000..ddfebcb
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/java/ldpath/BinaryNumericTestFunctionsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 ldpath;
+
+import at.newmedialab.ldpath.api.selectors.NodeSelector;
+import at.newmedialab.ldpath.parser.ParseException;
+import at.newmedialab.ldpath.parser.RdfPathParser;
+import core.AbstractTestBase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.rio.RDFFormat;
+import org.openrdf.rio.RDFParseException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+@RunWith(Parameterized.class)
+public class BinaryNumericTestFunctionsTest extends AbstractTestBase {
+
+    @Parameters(name = "fn:{0}")
+    public static List<String[]> data() {
+        ArrayList<String[]> data = new ArrayList<String[]>();
+        data.add(new String[] { "eq", "Eq", "Lt,Gt" });
+        data.add(new String[] { "ne", "Lt,Gt", "Eq" });
+        data.add(new String[] { "lt", "Lt", "Eq,Gt" });
+        data.add(new String[] { "le", "Lt,Eq", "Gt" });
+        data.add(new String[] { "gt", "Gt", "Eq,Lt" });
+        data.add(new String[] { "ge", "Eq,Gt", "Lt" });
+        return data;
+    }
+
+    @Parameter(0)
+    public String funktion;
+
+    @Parameter(1)
+    public String expected;
+
+    @Parameter(2)
+    public String forbidden;
+
+    @Before
+    public void loadData() throws RepositoryException, RDFParseException, IOException {
+        super.loadData("test-data.n3", RDFFormat.N3);
+    }
+
+    @Test
+    public void straight() throws ParseException {
+        runTest("fn:" + funktion, expected, forbidden);
+    }
+
+    @Test
+    public void inverse() throws ParseException {
+        runTest("!fn:" + funktion, forbidden, expected);
+    }
+
+    private void runTest(String fkt, String pos, String neg) throws ParseException {
+        final URI start = repository.getValueFactory().createURI("http://www.example.com/Compare");
+
+        ArrayList<Value> yes = new ArrayList<Value>();
+        for (String p : pos.split(",")) {
+            yes.add(repository.getValueFactory().createURI(NSS.get("ex") + p.trim()));
+        }
+        ArrayList<Value> no = new ArrayList<Value>();
+        for (String p : neg.split(",")) {
+            no.add(repository.getValueFactory().createURI(NSS.get("ex") + p.trim()));
+        }
+
+
+        final RdfPathParser<Value> parser = createParserFromString("ex:hasItem[" + fkt + "(foo:left, foo:right)]");
+        final NodeSelector<Value> sel = parser.parseSelector(NSS);
+
+        final Collection<Value> result = sel.select(backend, start, null, null);
+        assertEquals("items found", yes.size(), result.size());
+
+        for (Value value : yes) {
+            assertThat("matching resources", result, hasItem(value));
+        }
+        for (Value value : no) {
+            assertThat("not-matching resources", result, not(hasItem(value)));
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/java/ldpath/FunctionsTest.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/java/ldpath/FunctionsTest.java b/ldpath/ldpath-core/src/test/java/ldpath/FunctionsTest.java
new file mode 100644
index 0000000..861aa02
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/java/ldpath/FunctionsTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 ldpath;
+
+import at.newmedialab.ldpath.api.selectors.NodeSelector;
+import at.newmedialab.ldpath.model.fields.FieldMapping;
+import at.newmedialab.ldpath.parser.ParseException;
+import at.newmedialab.ldpath.parser.RdfPathParser;
+import core.AbstractTestBase;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.rio.RDFFormat;
+import org.openrdf.rio.RDFParseException;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+
+public class FunctionsTest extends AbstractTestBase {
+
+
+    @Before
+    public void loadData() throws RepositoryException, RDFParseException, IOException {
+        loadData("test-data.n3", RDFFormat.N3);
+    }
+
+    @Test
+    public void testConcat() throws ParseException {
+
+        RdfPathParser<Value> parser = createParserFromString("fn:concat(foo:title, \" \", foo:subtitle) :: xsd:string; ");
+        final URI context = repository.getValueFactory().createURI("http://www.example.com/1");
+
+        final FieldMapping<Object, Value> field = parser.parseRule(NSS);
+        final Collection<Object> result = field.getValues(backend, context);
+
+        assertThat(result, CoreMatchers.hasItem("One SubOne"));
+    }
+
+    @Test
+    public void testFirst() throws ParseException {
+        final URI ctx1 = repository.getValueFactory().createURI("http://www.example.com/1");
+        final URI ctx2 = repository.getValueFactory().createURI("http://www.example.com/2");
+
+        RdfPathParser<Value> parser = createParserFromString("fn:first(foo:not_valid, foo:title, foo:subtitle, foo:not_valid2) :: xsd:string; ");
+        final FieldMapping<Object, Value> field = parser.parseRule(NSS);
+
+        final Collection<Object> result = field.getValues(backend, ctx1);
+        assertEquals(1, result.size());
+        assertThat(result, CoreMatchers.hasItem("One"));
+
+        final Collection<Object> result2 = field.getValues(backend, ctx2);
+        assertEquals(1, result2.size());
+        assertThat(result2, CoreMatchers.hasItem("Two"));
+    }
+
+    @Test
+    public void testFirst2() throws ParseException {
+        final URI ctx1 = repository.getValueFactory().createURI("http://www.example.com/1");
+        final URI ctx2 = repository.getValueFactory().createURI("http://www.example.com/2");
+
+        RdfPathParser<Value> parser = createParserFromString("fn:first(foo:i) :: xsd:int; ");
+        final FieldMapping<Object, Value> field = parser.parseRule(NSS);
+
+        final Collection<Object> result = field.getValues(backend, ctx1);
+        assertEquals(3, result.size());
+        assertThat(result, CoreMatchers.hasItem(1));
+
+        final Collection<Object> result2 = field.getValues(backend, ctx2);
+        assertEquals(1, result2.size());
+        assertThat(result2, CoreMatchers.hasItem(99));
+    }
+
+    @Test
+    public void testLast() throws ParseException {
+        final URI ctx1 = repository.getValueFactory().createURI("http://www.example.com/1");
+        final URI ctx2 = repository.getValueFactory().createURI("http://www.example.com/2");
+
+        RdfPathParser<Value> parser = createParserFromString("fn:last(foo:not_valid, foo:title, foo:subtitle, foo:not_valid2) :: xsd:string; ");
+        final FieldMapping<Object, Value> field = parser.parseRule(NSS);
+
+        final Collection<Object> result = field.getValues(backend, ctx1);
+        assertEquals(1, result.size());
+        assertThat(result, CoreMatchers.hasItem("SubOne"));
+
+        final Collection<Object> result2 = field.getValues(backend, ctx2);
+        assertEquals(1, result2.size());
+        assertThat(result2, hasItem("SubTwo"));
+    }
+
+    @Test
+    public void testLast2() throws ParseException {
+        final URI ctx1 = repository.getValueFactory().createURI("http://www.example.com/1");
+        final URI ctx2 = repository.getValueFactory().createURI("http://www.example.com/2");
+
+        RdfPathParser<Value> parser = createParserFromString("fn:last(foo:i, ex:not_here) :: xsd:int; ");
+        final FieldMapping<Object, Value> field = parser.parseRule(NSS);
+
+        final Collection<Object> result = field.getValues(backend, ctx1);
+        assertEquals(3, result.size());
+        assertThat(result, CoreMatchers.<Object> hasItems(1, 2, 3));
+
+        final Collection<Object> result2 = field.getValues(backend, ctx2);
+        assertEquals(1, result2.size());
+        assertThat(result2, hasItem(99));
+    }
+
+    @Test
+    public void testEq() throws ParseException {
+        final URI start = repository.getValueFactory().createURI("http://www.example.com/start");
+        final URI ex1 = repository.getValueFactory().createURI("http://www.example.com/1");
+        final URI ex2 = repository.getValueFactory().createURI("http://www.example.com/2");
+
+        final RdfPathParser<Value> parser = createParserFromString("ex:hasItem[fn:eq(foo:i, foo:j)]");
+        final NodeSelector<Value> sel = parser.parseSelector(NSS);
+
+        final Collection<Value> result = sel.select(backend, start, null, null);
+        assertEquals(1, result.size());
+        assertThat(result, allOf(hasItem(ex2), not(hasItem(ex1))));
+
+        final RdfPathParser<Value> parseri = createParserFromString("ex:hasItem[! fn:eq(foo:i, foo:j)]");
+        final NodeSelector<Value> seli = parseri.parseSelector(NSS);
+
+        final Collection<Value> resulti = seli.select(backend, start, null, null);
+        assertEquals(1, resulti.size());
+        assertThat(resulti, allOf(hasItem(ex1), not(hasItem(ex2))));
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/java/parse/EmptyTestingBackend.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/java/parse/EmptyTestingBackend.java b/ldpath/ldpath-core/src/test/java/parse/EmptyTestingBackend.java
new file mode 100644
index 0000000..5e31d20
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/java/parse/EmptyTestingBackend.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 parse;
+
+import at.newmedialab.ldpath.model.backend.AbstractBackend;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.concurrent.ThreadPoolExecutor;
+
+
+public class EmptyTestingBackend extends
+		AbstractBackend<String> {
+	@Override
+	public boolean supportsThreading() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public ThreadPoolExecutor getThreadPool() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public boolean isLiteral(String n) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public boolean isURI(String n) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public boolean isBlank(String n) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public Locale getLiteralLanguage(String n) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public URI getLiteralType(String n) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String createLiteral(String content) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String createLiteral(String content, Locale language,
+			URI type) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String createURI(String uri) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Collection<String> listObjects(String subject,
+			String property) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Collection<String> listSubjects(String property,
+			String object) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String stringValue(String node) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/java/parse/ParserTest.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/java/parse/ParserTest.java b/ldpath/ldpath-core/src/test/java/parse/ParserTest.java
new file mode 100644
index 0000000..a048b41
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/java/parse/ParserTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 parse;
+
+import at.newmedialab.ldpath.api.backend.RDFBackend;
+import at.newmedialab.ldpath.api.tests.NodeTest;
+import at.newmedialab.ldpath.api.transformers.NodeTransformer;
+import at.newmedialab.ldpath.model.programs.Program;
+import at.newmedialab.ldpath.parser.ParseException;
+import at.newmedialab.ldpath.parser.RdfPathParser;
+import org.apache.commons.io.IOUtils;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+
+public class ParserTest {
+
+    private static RDFBackend<String> backend;
+    private static final String NS_TEST = "http://example.com/";
+    private static final String NS_FOO = "http://foo.com/some/path#";
+
+    private static final Map<String, String> NAMESPACES;
+    static {
+        Map<String, String> ns = new HashMap<String, String>();
+        ns.put("test", NS_TEST);
+        ns.put("foo", NS_FOO);
+        NAMESPACES = Collections.unmodifiableMap(ns);
+    }
+
+    @BeforeClass
+    public static void initClass() {
+        backend = new EmptyTestingBackend();
+    }
+
+
+    @Test
+    public void testParseProgram() throws IOException {
+        RdfPathParser<String> parser = createParser("program.txt");
+        try {
+            Program<String> program = parser.parseProgram();
+            assertNotNull(program.getField("path"));
+            assertNotNull(program.getField("lang_test"));
+            assertNotNull(program.getField("type_test"));
+            assertNotNull(program.getField("int_s"));
+            assertNotNull(program.getField("int_p"));
+            assertNotNull(program.getField("inverse"));
+            assertNotNull(program.getField("config"));
+            assertNotNull(program.getBooster());
+            assertNotNull(program.getFilter());
+
+        } catch (ParseException e) {
+            assertFalse(e.getMessage(), true);
+        }
+    }
+
+    @Test
+    public void testParseTest() throws IOException {
+        RdfPathParser<String> parser = createParser("test.txt");
+        try {
+            NodeTest<String> test = parser.parseTest(NAMESPACES);
+            assertNotNull(test);
+            assertNotNull(test.getPathExpression(backend));
+        } catch (ParseException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    @Test
+    public void testParsePrefixes() throws IOException {
+        RdfPathParser<String> parser = createParser("namespaces.txt");
+        try {
+            Map<String, String> prefixes = parser.parsePrefixes();
+            assertTrue(prefixes.containsKey("test"));
+            assertTrue(prefixes.containsKey("foo"));
+            assertEquals(NS_TEST, prefixes.get("test"));
+            assertEquals(NS_FOO, prefixes.get("foo"));
+        } catch (ParseException e) {
+            assertFalse(e.getMessage(), true);
+        }
+    }
+
+
+    private RdfPathParser<String> createParser(String input) throws IOException {
+        final URL resource = ParserTest.class.getResource(input);
+        Assume.assumeThat("Could not load test input data '" + input + "'", resource, CoreMatchers.notNullValue());
+
+        RdfPathParser<String> rdfPathParser = new RdfPathParser<String>(backend,new StringReader(IOUtils.toString(resource)));
+        rdfPathParser.registerTransformer(NS_TEST + "type", new NodeTransformer<String, String>() {
+            @Override
+            public String transform(RDFBackend<String> backend, String node)
+                    throws IllegalArgumentException {
+                return node;
+            }
+        });
+        return rdfPathParser;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/resources/ldpath/test-data.n3
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/resources/ldpath/test-data.n3 b/ldpath/ldpath-core/src/test/resources/ldpath/test-data.n3
new file mode 100644
index 0000000..c6c7b38
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/resources/ldpath/test-data.n3
@@ -0,0 +1,32 @@
+@prefix ex:	<http://www.example.com/> .
+@prefix foo: <http://localhost/vcab#> .
+
+ex:start a ex:Test;
+	ex:hasItem	ex:1, ex:2.
+
+ex:1 a ex:Item;
+	foo:title	"One";
+	foo:subtitle	"SubOne";
+	foo:i	"1", "2", "3";
+	foo:j	"2".
+	
+ex:2 a ex:Item;
+	foo:title	"Two";
+	foo:subtitle	"SubTwo";
+	foo:i	"99";
+	foo:j	"99".
+
+ex:Compare a ex:Test;
+	ex:hasItem ex:Eq, ex:Lt, ex:Gt.
+	
+ex:Eq a ex:Item;
+	foo:left	"5";
+	foo:right	"5".
+
+ex:Lt a ex:Item;
+	foo:left	"1";
+	foo:right	"5".
+
+ex:Gt a ex:Item;
+	foo:left	"9";
+	foo:right	"5".

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/resources/parse/namespaces.txt
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/resources/parse/namespaces.txt b/ldpath/ldpath-core/src/test/resources/parse/namespaces.txt
new file mode 100644
index 0000000..057b0a0
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/resources/parse/namespaces.txt
@@ -0,0 +1,5 @@
+@prefix skos: <http://www.w3.org/2004/02/skos/core#> 
+@prefix foo: <http://foo.com/some/path#> ;
+@prefix foaf: <http://xmlns.com/foaf/0.1/> ;
+@prefix test: <http://example.com/>
+@prefix dcterms: <http://purl.org/dc/terms/> ;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/resources/parse/program.txt
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/resources/parse/program.txt b/ldpath/ldpath-core/src/test/resources/parse/program.txt
new file mode 100644
index 0000000..1271b39
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/resources/parse/program.txt
@@ -0,0 +1,16 @@
+@prefix test: <http://example.com/>;
+@prefix foo: <http://foo.com/some/path#> ;
+
+@filter test:type is foo:bar | test:p1 & is-a test:Case ;
+
+@boost foo:boost / ^test:boost ;
+
+path = test:p1 / test:p2 :: test:type;
+lang_test = test:p1[@en] :: test:type;
+type_test = foo:p2[^^test:int] :: test:type;
+int_s = (foo:go)* :: test:type;
+int_p = (foo:go)+ :: test:type;
+
+inverse = ^test:incoming :: test:type;
+
+config = test:foo :: test:type(c1=true, c2="false", c3="1.234");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-core/src/test/resources/parse/test.txt
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-core/src/test/resources/parse/test.txt b/ldpath/ldpath-core/src/test/resources/parse/test.txt
new file mode 100644
index 0000000..0f8c4f4
--- /dev/null
+++ b/ldpath/ldpath-core/src/test/resources/parse/test.txt
@@ -0,0 +1 @@
+test:type is foo:bar | test:p1 & fn:eq("1", "2")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.classpath
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.classpath b/ldpath/ldpath-functions-collections/.classpath
new file mode 100644
index 0000000..0f53f3e
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.project
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.project b/ldpath/ldpath-functions-collections/.project
new file mode 100644
index 0000000..919dc87
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ldpath-functions-collections</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.settings/org.eclipse.core.resources.prefs
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.settings/org.eclipse.core.resources.prefs b/ldpath/ldpath-functions-collections/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..dc1b414
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.settings/org.eclipse.jdt.core.prefs
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.settings/org.eclipse.jdt.core.prefs b/ldpath/ldpath-functions-collections/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..60105c1
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.settings/org.eclipse.m2e.core.prefs
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.settings/org.eclipse.m2e.core.prefs b/ldpath/ldpath-functions-collections/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/.settings/org.eclipse.pde.core.prefs
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/.settings/org.eclipse.pde.core.prefs b/ldpath/ldpath-functions-collections/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..ba582c2
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,2 @@
+BUNDLE_ROOT_PATH=target/classes
+eclipse.preferences.version=1

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/pom.xml
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/pom.xml b/ldpath/ldpath-functions-collections/pom.xml
new file mode 100644
index 0000000..6436216
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/pom.xml
@@ -0,0 +1,107 @@
+<!--
+  ~ Copyright (c) 2013 Salzburg Research.
+  ~
+  ~  Licensed 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>at.newmedialab.ldpath</groupId>
+        <artifactId>ldpath-parent</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+
+    <name>LDPath Functions: rdf-Collections</name>
+
+    <artifactId>ldpath-functions-collections</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Collection-Functions for ldpath (rdf:Bag, rdf:Sequence, rdf:Collection)</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>at.newmedialab.ldpath</groupId>
+            <artifactId>ldpath-core</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>at.newmedialab.ldpath</groupId>
+            <artifactId>ldpath-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-repository-sail</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-sail-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>at.newmedialab.ldpath</groupId>
+            <artifactId>ldpath-backend-sesame</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-rio-n3</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Name>LDPath Functions: Math</Bundle-Name>
+                        <!-- Enable this for including your enhancement chain configuration -->
+                        <!-- <Install-Path>config</Install-Path> -->
+                        <Export-Package>
+                            at.newmedialab.ldpath.model.*;version=${project.version},
+                        </Export-Package>
+                        <_include>src/main/resources/META-INF/MANIFEST.MF</_include>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-scr-scrdescriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                        <configuration>
+                            <properties>
+                                <service.vendor>Salzburg Research</service.vendor>
+                            </properties>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/AbstractCollFunction.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/AbstractCollFunction.java b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/AbstractCollFunction.java
new file mode 100644
index 0000000..5fc0e4a
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/AbstractCollFunction.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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 at.newmedialab.ldpath.model.functions.coll;
+
+import at.newmedialab.ldpath.api.backend.RDFBackend;
+import at.newmedialab.ldpath.api.functions.SelectorFunction;
+
+import java.util.Collection;
+
+public abstract class AbstractCollFunction<Node> extends SelectorFunction<Node> {
+
+    protected static final String RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+
+    protected int getIndex(RDFBackend<Node> backend, Collection<Node> collection) {
+        if (collection.size() != 1) { throw new IllegalArgumentException("index must be a singel integer literal"); }
+        return backend.intValue(collection.iterator().next());
+    }
+
+    protected boolean hasType(RDFBackend<Node> backend, Node node, String type) {
+        return backend.listObjects(node, backend.createURI(RDF + "type")).contains(backend.createURI(type));
+    }
+
+    protected boolean isNil(RDFBackend<Node> backend, Node node) {
+        return backend.isURI(node) && backend.stringValue(node).equals(RDF + "nil");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/FlattenFunction.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/FlattenFunction.java b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/FlattenFunction.java
new file mode 100644
index 0000000..8050a64
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/FlattenFunction.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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 at.newmedialab.ldpath.model.functions.coll;
+
+import at.newmedialab.ldpath.api.backend.RDFBackend;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public class FlattenFunction<Node> extends AbstractCollFunction<Node> {
+
+
+    @Override
+    public Collection<Node> apply(RDFBackend<Node> backend, Node context, Collection<Node>... args) throws IllegalArgumentException {
+        if (args.length > 1) {
+            throw new IllegalArgumentException(getLocalName() + " must not have more than one parameter");
+        }
+        final Collection<Node> nodes;
+        if (args.length > 0) {
+            nodes = args[0];
+        } else {
+            nodes = Collections.singleton(context);
+        }
+
+        final List<Node> result = new LinkedList<Node>();
+        for (Node node : nodes) {
+            if (hasType(backend, node, RDF + "Bag")) {
+                flattenContainer(backend, node, result);
+            } else if (hasType(backend, node, RDF + "Seq")) {
+                flattenContainer(backend, node, result);
+            } else if (hasType(backend, node, RDF + "List")) {
+                flattenCollection(backend, node, result, new HashSet<Node>());
+            } else {
+                flattenCollection(backend, node, result, new HashSet<Node>());
+            }
+        }
+
+        return result;
+    }
+
+    private void flattenCollection(RDFBackend<Node> backend, Node node, Collection<Node> result, Collection<Node> backtrace) {
+        if (isNil(backend, node)) {
+            return;
+        }
+
+        // Stop if we detect a cycle.
+        if (!backtrace.add(node)) {
+            return;
+        }
+
+        // Add the (all) firsts
+        result.addAll(backend.listObjects(node, backend.createURI(RDF + "first")));
+
+        // Recursively add the rest
+        final Collection<Node> rest = backend.listObjects(node, backend.createURI(RDF + "rest"));
+        for (Node r : rest) {
+            flattenCollection(backend, r, result, backtrace);
+        }
+    }
+
+    private void flattenContainer(RDFBackend<Node> backend, Node node, Collection<Node> result) {
+        for (int i = 1; /* exit via break */; i++) {
+            final Collection<Node> objects = backend.listObjects(node, backend.createURI(RDF + "_" + i));
+            if (objects.size() > 0) {
+                result.addAll(objects);
+            } else {
+                break;
+            }
+        }
+    }
+
+    @Override
+    public String getSignature() {
+        return "fn:flatten([nodes: NodeList]) :: NodeList";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Flattens a rdf-Collection";
+    }
+
+    @Override
+    protected String getLocalName() {
+        return "flatten";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/GetFunction.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/GetFunction.java b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/GetFunction.java
new file mode 100644
index 0000000..ef993d0
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/GetFunction.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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 at.newmedialab.ldpath.model.functions.coll;
+
+import at.newmedialab.ldpath.api.backend.RDFBackend;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+public class GetFunction<Node> extends AbstractCollFunction<Node> {
+
+    @Override
+    public Collection<Node> apply(RDFBackend<Node> backend, Node context, Collection<Node>... args) throws IllegalArgumentException {
+        final Collection<Node> nodes;
+        final int index;
+        switch (args.length) {
+        case 1:
+            nodes = Collections.singleton(context);
+            index = getIndex(backend, args[0]);
+            break;
+        case 2:
+            nodes = args[0];
+            index = getIndex(backend, args[1]);
+            break;
+        default:
+            throw new IllegalArgumentException(getLocalName() + " must not have more than one or two parameters");
+        }
+
+        Collection<Node> result = new HashSet<Node>();
+        for (Node node : nodes) {
+            if (hasType(backend, node, RDF + "Bag")) {
+                result.addAll(getFromContainer(backend, node, index));
+            } else if (hasType(backend, node, RDF + "Seq")) {
+                result.addAll(getFromContainer(backend, node, index));
+            } else if (hasType(backend, node, RDF + "List")) {
+                result.addAll(getFromCollection(backend, node, index));
+            } else {
+                result.addAll(getFromCollection(backend, node, index));
+            }
+        }
+        return result;
+    }
+
+    private Collection<Node> getFromCollection(RDFBackend<Node> backend, Node node, int index) {
+        if (index < 0 || isNil(backend, node)) {
+            return Collections.emptySet();
+        } else if (index == 0) {
+            return backend.listObjects(node, backend.createURI(RDF + "first"));
+        } else {
+            HashSet<Node> result = new HashSet<Node>();
+            for (Node n : backend.listObjects(node, backend.createURI(RDF + "rest"))) {
+                result.addAll(getFromCollection(backend, n, index - 1));
+            }
+            return result;
+        }
+    }
+
+    private Collection<Node> getFromContainer(RDFBackend<Node> backend, Node node, int index) {
+        return backend.listObjects(node, backend.createURI(RDF + "_" + (index + 1)));
+    }
+
+    @Override
+    public String getSignature() {
+        return "fn:get([nodes: NodeList,] n: Integer) :: NodeList";
+    }
+
+    @Override
+    public String getDescription() {
+        return "retrieve the nth element from a rdf-Collection (0-based)";
+    }
+
+    @Override
+    protected String getLocalName() {
+        return "get";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/SubListFunction.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/SubListFunction.java b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/SubListFunction.java
new file mode 100644
index 0000000..0e27333
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/java/at/newmedialab/ldpath/model/functions/coll/SubListFunction.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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 at.newmedialab.ldpath.model.functions.coll;
+
+import at.newmedialab.ldpath.api.backend.RDFBackend;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SubListFunction<Node> extends AbstractCollFunction<Node> {
+
+    @Override
+    public Collection<Node> apply(RDFBackend<Node> backend, Node context, Collection<Node>... args) throws IllegalArgumentException {
+        final Collection<Node> nodes;
+        final int start, end;
+        switch (args.length) {
+        case 1:
+            nodes = Collections.singleton(context);
+            start = getIndex(backend, args[0]);
+            end = Integer.MAX_VALUE;
+            break;
+        case 2:
+            if (args[0].size() == 1 && backend.isLiteral(args[0].iterator().next())) {
+                nodes = Collections.singleton(context);
+                start = getIndex(backend, args[0]);
+                end = getIndex(backend, args[1]);
+            } else {
+                nodes = args[0];
+                start = getIndex(backend, args[1]);
+                end = Integer.MAX_VALUE;
+            }
+            break;
+        case 3:
+            nodes = args[0];
+            start = getIndex(backend, args[1]);
+            end = getIndex(backend, args[2]);
+            break;
+        default:
+            throw new IllegalArgumentException(getLocalName() + " takes at most 3 arguments");
+        }
+
+        final List<Node> result = new LinkedList<Node>();
+        for (Node node : nodes) {
+            if (hasType(backend, node, RDF + "Bag")) {
+                result.addAll(subListFromContainer(backend, node, start, end));
+            } else if (hasType(backend, node, RDF + "Seq")) {
+                result.addAll(subListFromContainer(backend, node, start, end));
+            } else if (hasType(backend, node, RDF + "List")) {
+                subListFromCollection(backend, node, start, end, result);
+            } else {
+                subListFromCollection(backend, node, start, end, result);
+            }
+        }
+
+        return result;
+    }
+
+    private void subListFromCollection(RDFBackend<Node> backend, Node node, int start, int end, List<Node> result) {
+        if (isNil(backend, node)) { return; }
+
+        if (end > 0) {
+            if (start <= 0) {
+                result.addAll(backend.listObjects(node, backend.createURI(RDF + "first")));
+            }
+            for (Node n : backend.listObjects(node, backend.createURI(RDF + "rest"))) {
+                subListFromCollection(backend, n, start - 1, end - 1, result);
+            }
+        }
+
+    }
+
+    private Collection<? extends Node> subListFromContainer(RDFBackend<Node> backend, Node node, int start, int end) {
+        List<Node> result = new LinkedList<Node>();
+        for (int i = start; i < end; i++) {
+            final Collection<Node> objects = backend.listObjects(node, backend.createURI(RDF + "_" + (i + 1)));
+            if (objects.size() > 0) {
+                result.addAll(objects);
+            } else {
+                break;
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public String getSignature() {
+        return "fn:subList([nodes: NodeList,] start: Integer [, end: Integer]) :: NodeList";
+    }
+
+    @Override
+    public String getDescription() {
+        return "select a range from a rdf-Collection (like substring does for strings, 0-based)";
+    }
+
+    @Override
+    protected String getLocalName() {
+        return "subList";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/resources/META-INF/MANIFEST.MF
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/resources/META-INF/MANIFEST.MF b/ldpath/ldpath-functions-collections/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7547fd6
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+SPI-Provider: *

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction b/ldpath/ldpath-functions-collections/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
new file mode 100644
index 0000000..8c0f779
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/main/resources/META-INF/services/at.newmedialab.ldpath.api.functions.SelectorFunction
@@ -0,0 +1,3 @@
+at.newmedialab.ldpath.model.functions.coll.FlattenFunction
+at.newmedialab.ldpath.model.functions.coll.GetFunction
+at.newmedialab.ldpath.model.functions.coll.SubListFunction
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/32a909f7/ldpath/ldpath-functions-collections/src/test/java/coll/CollectionsTest.java
----------------------------------------------------------------------
diff --git a/ldpath/ldpath-functions-collections/src/test/java/coll/CollectionsTest.java b/ldpath/ldpath-functions-collections/src/test/java/coll/CollectionsTest.java
new file mode 100644
index 0000000..85889ad
--- /dev/null
+++ b/ldpath/ldpath-functions-collections/src/test/java/coll/CollectionsTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013 Salzburg Research.
+ *
+ *  Licensed 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 coll;
+
+
+import core.AbstractTestBase;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.rio.RDFFormat;
+import org.openrdf.rio.RDFParseException;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class CollectionsTest extends AbstractTestBase {
+
+    private static final int BAG_SIZE = 4;
+
+    @Parameters(name = "{0}")
+    public static List<String[]> data() {
+        return Arrays.asList(
+                new String[] { "ex:List" },
+                new String[] { "ex:Bag" },
+                new String[] { "ex:Seq" }
+                );
+    }
+
+    @Parameter
+    public String testContextUri;
+
+    private URI uri;
+
+    @Before
+    public void loadData() throws RepositoryException, RDFParseException, IOException {
+        super.loadData("data.n3", RDFFormat.N3);
+
+        uri = createURI(testContextUri);
+    }
+
+    @Test
+    public void testFlatten() throws Exception {
+        for (String ldPath : Arrays.asList("foo:hasItems / fn:flatten()", "fn:flatten(foo:hasItems)")) {
+            final Collection<Value> nodes = evaluateSelector(ldPath, uri);
+            Assert.assertEquals(ldPath, BAG_SIZE, nodes.size());
+            Assert.assertThat(ldPath, nodes,
+                    CoreMatchers.<Value> hasItems(createURI("ex:1"), createURI("ex:2"), createURI("ex:3"), createURI("ex:4")));
+        }
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        for (int i = 0; i < BAG_SIZE + 1; i++) {
+            for ( String pattern : Arrays.asList("foo:hasItems / fn:get(\"%d\")", "fn:get(foo:hasItems,\"%d\")")) {
+                final String ldPath = String.format(pattern, i);
+                final Collection<Value> nodes = evaluateSelector(ldPath, uri);
+                if (i < BAG_SIZE) {
+                    Assert.assertEquals(ldPath, 1, nodes.size());
+                    Assert.assertThat(ldPath, nodes,
+                            CoreMatchers.<Value> hasItem(createURI("ex", String.valueOf(i + 1))));
+                } else {
+                    Assert.assertEquals(ldPath, 0, nodes.size());
+                }
+
+            }
+        }
+    }
+
+    @Test
+    public void testSubList() throws Exception {
+        for (int i = 0; i <= BAG_SIZE + 1; i++) {
+            for (String pattern : Arrays.asList("foo:hasItems / fn:subList(", "fn:subList(foo:hasItems, ")) {
+                final String ldPath_1A = String.format("%s\"%d\")", pattern, i);
+                final Collection<Value> nodes_1A = evaluateSelector(ldPath_1A, uri);
+
+                final int expStart = Math.max(0, Math.min(i, BAG_SIZE));
+
+                Assert.assertEquals(ldPath_1A, Math.max(BAG_SIZE - expStart, 0), nodes_1A.size());
+                for (int e = expStart; e < BAG_SIZE; e++) {
+                    Assert.assertThat(ldPath_1A, nodes_1A,
+                            CoreMatchers.<Value> hasItem(createURI("ex", String.valueOf(e + 1))));
+                }
+                for (int j = i; j <= BAG_SIZE + 1; j++) {
+                    final String ldPath_2A = String.format("%s\"%d\", \"%d\")", pattern, i, j);
+                    final Collection<Value> nodes_2A = evaluateSelector(ldPath_2A, uri);
+
+                    final int expEnd = Math.max(0, Math.min(j, BAG_SIZE));
+
+                    Assert.assertEquals(ldPath_2A, expEnd - expStart, nodes_2A.size());
+                    for (int e = expStart; e < expEnd; e++) {
+                        Assert.assertThat(ldPath_2A, nodes_2A,
+                                CoreMatchers.<Value> hasItem(createURI("ex", String.valueOf(e + 1))));
+                    }
+                }
+            }
+        }
+    }
+}