From 08dd818774e82caaeb4d418e4d7398ed5805de27 Mon Sep 17 00:00:00 2001 From: "fabio.simeoni" Date: Tue, 8 Jan 2013 08:12:00 +0000 Subject: [PATCH] 1.x branch (first created for gCube 2.12) git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/information-system/discovery-client/1.0@67186 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 36 ++++ .project | 23 +++ distro/INSTALL | 1 + distro/LICENSE | 6 + distro/MAINTAINERS | 2 + distro/README | 39 ++++ distro/changelog.xml | 5 + distro/descriptor.xml | 38 ++++ distro/profile.xml | 26 +++ distro/svnpath.txt | 1 + pom.xml | 103 ++++++++++ .../discovery/client/api/DiscoveryClient.java | 41 ++++ .../client/api/DiscoveryException.java | 41 ++++ .../client/api/InvalidResultException.java | 37 ++++ .../discovery/client/api/ResultParser.java | 25 +++ .../discovery/client/impl/DelegateClient.java | 101 ++++++++++ .../discovery/client/impl/JAXBParser.java | 53 +++++ .../discovery/client/queries/api/Query.java | 19 ++ .../client/queries/api/SimpleQuery.java | 45 +++++ .../client/queries/impl/QueryBox.java | 52 +++++ .../client/queries/impl/QueryTemplate.java | 184 ++++++++++++++++++ .../discovery/client/queries/impl/Utils.java | 9 + .../discovery/client/queries/impl/XQuery.java | 92 +++++++++ 23 files changed, 979 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 distro/INSTALL create mode 100644 distro/LICENSE create mode 100644 distro/MAINTAINERS create mode 100644 distro/README create mode 100644 distro/changelog.xml create mode 100644 distro/descriptor.xml create mode 100644 distro/profile.xml create mode 100644 distro/svnpath.txt create mode 100644 pom.xml create mode 100644 src/main/java/org/gcube/resources/discovery/client/api/DiscoveryClient.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/api/DiscoveryException.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/api/InvalidResultException.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/api/ResultParser.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/impl/DelegateClient.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/impl/JAXBParser.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/api/Query.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/api/SimpleQuery.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryBox.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryTemplate.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/impl/Utils.java create mode 100644 src/main/java/org/gcube/resources/discovery/client/queries/impl/XQuery.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..534b5e5 --- /dev/null +++ b/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..59f4c26 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + discovery-client + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/distro/INSTALL b/distro/INSTALL new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/distro/INSTALL @@ -0,0 +1 @@ + diff --git a/distro/LICENSE b/distro/LICENSE new file mode 100644 index 0000000..630ba97 --- /dev/null +++ b/distro/LICENSE @@ -0,0 +1,6 @@ +gCube System - License +------------------------------------------------------------ + +The gCube/gCore software is licensed as Free Open Source software conveying to the EUPL (http://ec.europa.eu/idabc/eupl). +The software and documentation is provided by its authors/distributors "as is" and no expressed or +implied warranty is given for its use, quality or fitness for a particular case. diff --git a/distro/MAINTAINERS b/distro/MAINTAINERS new file mode 100644 index 0000000..e58db00 --- /dev/null +++ b/distro/MAINTAINERS @@ -0,0 +1,2 @@ +* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy +* Lucio Lelii (lucio.lelii@isti.cnr.it), CNR, Italy \ No newline at end of file diff --git a/distro/README b/distro/README new file mode 100644 index 0000000..e14fefb --- /dev/null +++ b/distro/README @@ -0,0 +1,39 @@ +The gCube System - ${name} +---------------------- + +This work has been partially supported by the following European projects: DILIGENT (FP6-2003-IST-2), D4Science (FP7-INFRA-2007-1.2.2), +D4Science-II (FP7-INFRA-2008-1.2.2), iMarine (FP7-INFRASTRUCTURES-2011-2), and EUBrazilOpenBio (FP7-ICT-2011-EU-Brazil). + +Authors +------- + +* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy +* Lucio Lelii (lucio.lelii@isti.cnr.it), CNR, Italy + +Version and Release Date +------------------------ +${version} + +Description +----------- +${description} + +Download information +-------------------- + +Source code is available from SVN: +${scm.url} + +Binaries can be downloaded from: + + +Documentation +------------- +Documentation is available on-line from the Projects Documentation Wiki: +https://gcube.wiki.gcube-system.org/gcube/index.php + + +Licensing +--------- + +This software is licensed under the terms you may find in the file named "LICENSE" in this directory. diff --git a/distro/changelog.xml b/distro/changelog.xml new file mode 100644 index 0000000..cec4f71 --- /dev/null +++ b/distro/changelog.xml @@ -0,0 +1,5 @@ + + + First Release + + \ No newline at end of file diff --git a/distro/descriptor.xml b/distro/descriptor.xml new file mode 100644 index 0000000..06c416f --- /dev/null +++ b/distro/descriptor.xml @@ -0,0 +1,38 @@ + + servicearchive + + tar.gz + + / + + + ${distroDirectory} + / + true + + README + LICENSE + INSTALL + MAINTAINERS + changelog.xml + profile.xml + + 755 + true + + + + + target/${build.finalName}.jar + /${artifactId} + + + ${distroDirectory}/svnpath.txt + /${artifactId} + true + + + \ No newline at end of file diff --git a/distro/profile.xml b/distro/profile.xml new file mode 100644 index 0000000..f6970f3 --- /dev/null +++ b/distro/profile.xml @@ -0,0 +1,26 @@ + + + + Service + + ${description} + InformationSystem + ${artifactId} + 1.0.0 + + + ${artifactId} + ${version} + + ${groupId} + ${artifactId} + ${version} + + + ${build.finalName}.jar + + + + + + diff --git a/distro/svnpath.txt b/distro/svnpath.txt new file mode 100644 index 0000000..f416f9d --- /dev/null +++ b/distro/svnpath.txt @@ -0,0 +1 @@ +${scm.url} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7d5b21c --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ + + 4.0.0 + + + org.gcube.tools + maven-parent + 1.0.0 + + + + org.gcube.resources.discovery + discovery-client + 1.0.0-SNAPSHOT + Discovery Client + Base API for resource discovery clients + + + scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId} + scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId} + http://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId} + + + + distro + + + + + + org.gcube.data.access + streams + [2.0.0-SNAPSHOT,3.0.0-SNAPSHOT) + + + + org.slf4j + slf4j-api + 1.6.4 + + + + org.slf4j + slf4j-simple + 1.6.4 + test + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-profile + install + + copy-resources + + + target + + + ${distroDirectory} + true + + profile.xml + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + ${distroDirectory}/descriptor.xml + + + + + servicearchive + install + + single + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryClient.java b/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryClient.java new file mode 100644 index 0000000..3bfff1c --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryClient.java @@ -0,0 +1,41 @@ +package org.gcube.resources.discovery.client.api; + +import java.util.List; + +import org.gcube.data.streams.Stream; +import org.gcube.resources.discovery.client.queries.api.Query; + +/** + * Local interface for resource discovery. + *

+ * Submits {@link Query}s for remote execution and returns a list of typed results. + * + * @author Fabio Simeoni + * + * @param the type of query results + * + */ +public interface DiscoveryClient { + + /** + * Submits a {@link Query} for remote execution and returns a list of typed results. + * + * @param query the query + * @return the results + * @throws DiscoveryException if the query cannot be submitted + * @throws InvalidResultException if the results cannot be parsed. Implementations may adopt different degrees of + * tolerance to parsing errors before raising this exception. + */ + List submit(Query query) throws DiscoveryException, InvalidResultException; + + /** + * Submits a {@link Query} for remote execution and returns a {@link Stream} of typed results. + *

+ * Parsing errors may and should be delivered as {@link InvalidResultException}s during stream iteration. + * + * @param query the query + * @return the results + * @throws DiscoveryException if the query cannot be submitted + */ + Stream submitForStream(Query query) throws DiscoveryException; +} diff --git a/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryException.java b/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryException.java new file mode 100644 index 0000000..34adfc0 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/api/DiscoveryException.java @@ -0,0 +1,41 @@ +package org.gcube.resources.discovery.client.api; + + +/** + * Raised when services endpoints cannot be discovered. + * + * @author Fabio Simeoni + * + */ +public class DiscoveryException extends RuntimeException { + + + private static final long serialVersionUID = 1L; + + /** + * Creates an instance with a message. + * @param msg the message + */ + public DiscoveryException(String msg) { + super(msg); + } + + /** + * Creates an instance from a cause. + * @param cause the cause + */ + public DiscoveryException(Throwable cause) { + super(cause); + } + + /** + * Creates an instance from a message and a cause. + * @param msg the message + * @param cause the cause + */ + public DiscoveryException(String msg,Throwable cause) { + super(msg,cause); + } + + +} diff --git a/src/main/java/org/gcube/resources/discovery/client/api/InvalidResultException.java b/src/main/java/org/gcube/resources/discovery/client/api/InvalidResultException.java new file mode 100644 index 0000000..bf255ad --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/api/InvalidResultException.java @@ -0,0 +1,37 @@ +package org.gcube.resources.discovery.client.api; + +/** + * Raised by {@link DiscoveryClient}s for result parsing errors. + * + * @author Fabio Simeoni + * + */ +public class InvalidResultException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Creates an instance with a given message. + * @param msg the message + */ + public InvalidResultException(String msg) { + super(msg); + } + + /** + * Creates an instance with a given cause. + * @param cause the cause + */ + public InvalidResultException(Throwable cause) { + super(cause); + } + + /** + * Creates an instance with a given message and a given cause. + * @param msg the message + * @param cause the cause + */ + public InvalidResultException(String msg, Throwable cause) { + super(msg,cause); + } +} diff --git a/src/main/java/org/gcube/resources/discovery/client/api/ResultParser.java b/src/main/java/org/gcube/resources/discovery/client/api/ResultParser.java new file mode 100644 index 0000000..d966e50 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/api/ResultParser.java @@ -0,0 +1,25 @@ +package org.gcube.resources.discovery.client.api; + +import org.gcube.resources.discovery.client.queries.api.Query; + +/** + * Transforms untyped results into typed results. + * + * @author Fabio Simeoni + * + * @param the result type + * + * @see DiscoveryClient + * @see Query + */ +public interface ResultParser { + + /** + * Transforms an untyped result. + * + * @param result the untyped results + * @return the typed result + * @throws Exception if the result cannot be typed + */ + R parse(String result) throws Exception; +} diff --git a/src/main/java/org/gcube/resources/discovery/client/impl/DelegateClient.java b/src/main/java/org/gcube/resources/discovery/client/impl/DelegateClient.java new file mode 100644 index 0000000..3a16ce5 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/impl/DelegateClient.java @@ -0,0 +1,101 @@ +package org.gcube.resources.discovery.client.impl; + +import static org.gcube.data.streams.dsl.Streams.*; + +import java.util.ArrayList; +import java.util.List; + +import org.gcube.data.streams.Stream; +import org.gcube.data.streams.exceptions.StreamSkipSignal; +import org.gcube.data.streams.exceptions.StreamStopSignal; +import org.gcube.data.streams.generators.Generator; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.api.DiscoveryException; +import org.gcube.resources.discovery.client.api.InvalidResultException; +import org.gcube.resources.discovery.client.api.ResultParser; +import org.gcube.resources.discovery.client.queries.api.Query; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link DiscoveryClient} that delegates the execution of queries to another {@link DiscoveryClient} that + * does not perform result parsing and the parsing itself to a dedicated {@link ResultParser}. + * + * + * @author Fabio Simeoni + * + * @param the type of query results + */ +public class DelegateClient implements DiscoveryClient { + + private static Logger log = LoggerFactory.getLogger(DelegateClient.class); + + private final ResultParser parser; + private final DiscoveryClient inner; + + /** + * Creates an instance with a given {@link ResultParser} and a {@link DiscoveryClient} that produces untyped results + * @param parser the parser + * @param inner the client + */ + public DelegateClient(ResultParser parser,DiscoveryClient inner) { + this.parser=parser; + this.inner=inner; + } + + /** + * {@inheritDoc} + *

+ * Result parsing errors are only logged as long as some results are successfully parsed. Otherwise, the + * client flags the parsing errors as likely due to the parser itself. + */ + public List submit(Query query) throws DiscoveryException, InvalidResultException { + + List parsed = new ArrayList(); + + List unparsed = inner.submit(query); + + int errors = 0; + + for (String result : unparsed) + try { + parsed.add(parser.parse(result)); + } + catch(Exception e) { + log.warn("discarded invalid result "+result,e); + errors++; + } + + if (errors>0 && parsed.size()==0) + throw new InvalidResultException("no success but "+errors+" errors parsing results"); + + return parsed; + } + + /** + * {@inheritDoc} + *

+ * Result parsing errors are only logged as long as they do results are successfully parsed. Otherwise, the + * client flags the parsing errors as likely due to the parser itself. + */ + public Stream submitForStream(Query query) throws DiscoveryException { + + Stream unparsed = inner.submitForStream(query); + + return pipe(unparsed).through(new ParsingGenerator()); + + } + + //helper + private class ParsingGenerator implements Generator { + + public R yield(String result) throws StreamSkipSignal, StreamStopSignal { + try { + return parser.parse(result); + } + catch(Exception e) { + throw new InvalidResultException(e); + } + } + } +} diff --git a/src/main/java/org/gcube/resources/discovery/client/impl/JAXBParser.java b/src/main/java/org/gcube/resources/discovery/client/impl/JAXBParser.java new file mode 100644 index 0000000..23d48f0 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/impl/JAXBParser.java @@ -0,0 +1,53 @@ +package org.gcube.resources.discovery.client.impl; + +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; + +import org.gcube.resources.discovery.client.api.ResultParser; + +/** + * A {@link ResultParser} that parses query results into JAXB annotated classes. + * + * @author Fabio Simeoni + * + * @param the type of parsed results + */ +public class JAXBParser implements ResultParser { + + private final Class type; + private final Unmarshaller um; + + //caches contexts per type + private static Map,JAXBContext> ctxts = new HashMap, JAXBContext>(); + + /** + * Creates an instance with a JAXB-annotated class. + * @param type the class + */ + public JAXBParser(Class type) { + + this.type=type; + + //lazily create unmarshaller for this type + try { + JAXBContext ctx = ctxts.get(type); + if (ctx==null) { + ctx = JAXBContext.newInstance(type); + ctxts.put(type,ctx); + } + this.um=ctx.createUnmarshaller(); + } + catch(Exception e) { + throw new RuntimeException("error with parser",e); + } + } + + public R parse(String result) throws Exception { + return type.cast(um.unmarshal(new StringReader(result))); + + } +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/api/Query.java b/src/main/java/org/gcube/resources/discovery/client/queries/api/Query.java new file mode 100644 index 0000000..25e6eb7 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/api/Query.java @@ -0,0 +1,19 @@ +package org.gcube.resources.discovery.client.queries.api; + + + +/** + * A query for resources. + *

+ * The interface is intended for clients that consume queries and are not otherwise concerned with their construction. + * + */ +public interface Query { + + /** + * Returns the textual expression of the query. + * @return the expression. + */ + public String expression(); + +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/api/SimpleQuery.java b/src/main/java/org/gcube/resources/discovery/client/queries/api/SimpleQuery.java new file mode 100644 index 0000000..f4d5c92 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/api/SimpleQuery.java @@ -0,0 +1,45 @@ +package org.gcube.resources.discovery.client.queries.api; + +import java.net.URI; + + + +/** + * A {@link Query} that can be customised with namespace declarations, conditions on results, and result expressions. + * + */ +public interface SimpleQuery extends Query { + + /** + * Adds a variable to the query. + * + * @param name the name of the variable + * @param range the range of the variable + * @return the query + */ + SimpleQuery addVariable(String name, String range); + + /** + * Adds a free-form condition on query results. + * + * @param condition the condition + * @return the query + */ + SimpleQuery addCondition(String condition); + + /** + * Adds a namespace to the query. + * @param prefix the namespace prefix + * @param uri the namespace URI + * @return the query + */ + SimpleQuery addNamespace(String prefix, URI uri); + + /** + * Adds a result expression to the query. + * @param expression the result expression + * @return the query + */ + SimpleQuery setResult(String expression); + +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryBox.java b/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryBox.java new file mode 100644 index 0000000..c78a59d --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryBox.java @@ -0,0 +1,52 @@ +package org.gcube.resources.discovery.client.queries.impl; + +import static org.gcube.resources.discovery.client.queries.impl.Utils.*; + +import org.gcube.resources.discovery.client.queries.api.Query; + +public class QueryBox implements Query { + + private final String expression; + + public QueryBox(String expression) { + notNull("expression",expression); + this.expression=expression; + } + + public String expression() { + return expression; + } + + @Override + public String toString() { + return super.toString()+"="+expression(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((expression == null) ? 0 : expression.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QueryBox other = (QueryBox) obj; + if (expression == null) { + if (other.expression != null) + return false; + } else if (!expression.equals(other.expression)) + return false; + return true; + } + + + +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryTemplate.java b/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryTemplate.java new file mode 100644 index 0000000..de3a2c3 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/impl/QueryTemplate.java @@ -0,0 +1,184 @@ +package org.gcube.resources.discovery.client.queries.impl; + +import static javax.xml.stream.XMLStreamConstants.*; +import static org.gcube.resources.discovery.client.queries.impl.Utils.*; + +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + +import org.gcube.resources.discovery.client.queries.api.Query; + +/** + * A {@link Query} that interpolates named parameters inside a template. + *

+ * Templates are strings with empty XML elements, optionally with a {@link #DEFAULT} attribute, e.g.: + *

+ * all results that satisfy <cond1/> or <cond2 def='that'/> <extra/> + *

+ * Whenever {@link #expression()} is invoked, the elements in the template are replaced according to the first rule that applies + * among the following: + * + *

+ * + * For example, given the previous template and the single parameter cond1="this", {@link #expression()} returns: + *

+ * all results that satisfy this or that + *

+ * + */ +public class QueryTemplate extends QueryBox implements Query { + + public static final String DEFAULT = "def"; + + + private static final XMLInputFactory xmlif = XMLInputFactory.newInstance(); + + private static final String wrapper = "_template_"; + + + private final Map parameters; + + /** + * Creates an instance with a template. + * + * @param template the template + */ + public QueryTemplate(String template) { + super(template); + this.parameters = new HashMap(); + } + + /** + * Creates an instance with a template and an initial set of parameters. + * + * @param template the template + */ + public QueryTemplate(String template, Map parameters) { + super(template); + notNull("parameters", parameters); + this.parameters = new HashMap(parameters); + } + + public String expression() { + return interpolate(super.expression(), parameters); + } + + /** + * Adds a parameter to the query, overwriting any value that it may already have. + * + * @param name the parameter name + * @param value the parameter value + * @throws IllegalStateException if the parameter name or value are null + */ + public void addParameter(String name, String value) { + + notNull("name",name); + notNull("value",value); + + this.parameters.put(name, value); + } + + /** + * Adds a parameter to the query, extending any value that it may already have. + * + * @param name the parameter name + * @param value the value + * @throws IllegalStateException if the parameter name or value are null + */ + public void appendParameter(String name, String value) { + + notNull("name",name); + notNull("value",value); + + + if (parameters.containsKey(name)) + value=parameters.get(name)+value; + + parameters.put(name, value); + } + + /** + * Returns the current value of a parameter. + * @param name the parameter name + * @return the value + * @throws IllegalStateException if the parameter does not exist + * @throws IllegalStateException if the parameter name is null + */ + public String parameter(String name) throws IllegalStateException { + + notNull("name",name); + + if (hasParameter(name)) + return parameters.get(name); + + throw new IllegalStateException("unknown parameter "+name); + } + + /** + * Returns true if the query has a given parameter. + * @param name the parameter name + * @return true if the query has a given parameter, false otherwise + * @throws IllegalStateException if the parameter name is null + */ + public boolean hasParameter(String name) { + + notNull("name",name); + + return parameters.containsKey(name); + } + + // helper + private String interpolate(String expression, Map parameters) { + // replace query parameters with their values. + try { + + StringBuilder builder = new StringBuilder(); + + XMLStreamReader xmlr = xmlif.createXMLStreamReader(new StringReader("<"+wrapper+">"+expression+"")); + + loop: while (true) { + + int tokenType = xmlr.next(); + + switch (tokenType) { + + case START_ELEMENT: // replace parameters with values (provided or default) + + String name = xmlr.getLocalName(); + + if (name.equals(wrapper)) + break; + + if (parameters.containsKey(name)) + builder.append(parameters.get(name)); + else { + // is there a default value? + String def = xmlr.getAttributeValue(null,DEFAULT); + if (def != null) + // add default as a parameter + builder.append(def); + } + break; + + case CHARACTERS: // copy text in output + builder.append(xmlr.getText()); + break; + + case END_DOCUMENT: + break loop; + } + } + return builder.toString(); + } catch (Exception e) { + throw new RuntimeException("cannot replace parameters " + parameters + " in query " + expression,e); + } + } +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/impl/Utils.java b/src/main/java/org/gcube/resources/discovery/client/queries/impl/Utils.java new file mode 100644 index 0000000..f2c2910 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/impl/Utils.java @@ -0,0 +1,9 @@ +package org.gcube.resources.discovery.client.queries.impl; + +public class Utils { + + public static void notNull(String name, Object value) throws IllegalArgumentException { + if (value==null) + throw new IllegalArgumentException("parameter "+ name+" is null"); + } +} diff --git a/src/main/java/org/gcube/resources/discovery/client/queries/impl/XQuery.java b/src/main/java/org/gcube/resources/discovery/client/queries/impl/XQuery.java new file mode 100644 index 0000000..4760880 --- /dev/null +++ b/src/main/java/org/gcube/resources/discovery/client/queries/impl/XQuery.java @@ -0,0 +1,92 @@ +package org.gcube.resources.discovery.client.queries.impl; + +import java.net.URI; +import java.util.Map; + +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; + +/** + * A {@link SimpleQuery} over an XQuery template. + *

+ * The template is defined as follows (cf. {@link #template}): + *

+ * <ns/> for $resource in <range/><vars/> where <cond def="$result"/> return <result def="$result"/> + *

+ * + * where: + * + *

+ * + * @author Fabio Simeoni + * + */ +public class XQuery extends QueryTemplate implements SimpleQuery { + + public static final String ns = "ns"; + public static final String vars = "vars"; + public static final String range = "range"; + public static final String cond = "cond"; + public static final String result = "result"; + + public static final String template = " for $resource in <" + range + "/> where <" + cond + " " + DEFAULT + + "='$resource'/> return <" + result + " " + DEFAULT + "='$resource'/>"; + + public XQuery(Map parameters) {// add static parameters + + super(template, parameters); + + } + + /** + * {@inheritDoc} + *

+ * In the condition, $resource ranges over resources. + * + */ + public XQuery addCondition(String condition) { + + String newcond = "("+condition+")"; + + if (hasParameter(cond)) + appendParameter(cond," and "+newcond); + else + addParameter(cond,newcond); + + + return this; + } + + public XQuery addNamespace(String prefix, URI uri) { + + String declaration = "declare namespace " + prefix + " = '" + uri + "';"; + + appendParameter(ns,declaration); + + return this; + } + + public XQuery addVariable(String variable, String range) { + + String declaration = ", "+variable+" in "+range; + + appendParameter(vars,declaration); + + return this; + } + + /** + * {@inheritDoc} + *

+ * In the expression, $resource ranges over resources. + */ + public XQuery setResult(String expression) { + addParameter(result, expression); + return this; + } + +}