Merge remote-tracking branch 'origin/unstable' into testing
[gpl/argeo-suite.git] / org.argeo.app.core / src / org / argeo / app / core / XPathUtils.java
diff --git a/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java b/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java
new file mode 100644 (file)
index 0000000..f86445c
--- /dev/null
@@ -0,0 +1,176 @@
+package org.argeo.app.core;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.argeo.api.cms.CmsLog;
+
+/** Ease XPath generation for JCR requests */
+public class XPathUtils {
+       private final static CmsLog log = CmsLog.getLog(XPathUtils.class);
+
+       private final static String QUERY_XPATH = "xpath";
+
+       public static String descendantFrom(String parentPath) {
+               if (notEmpty(parentPath)) {
+                       if ("/".equals(parentPath))
+                               parentPath = "";
+                       // Hardcoded dependency to Jackrabbit. Remove
+                       String result = "/jcr:root" + ISO9075.encodePath(parentPath);
+                       if (log.isTraceEnabled()) {
+                               String result2 = "/jcr:root" + parentPath;
+                               if (!result2.equals(result))
+                                       log.warn("Encoded Path " + result2 + " --> " + result);
+                       }
+                       return result;
+               } else
+                       return "";
+       }
+
+       public static String localAnd(String... conditions) {
+               StringBuilder builder = new StringBuilder();
+               for (String condition : conditions) {
+                       if (notEmpty(condition)) {
+                               builder.append(" ").append(condition).append(" and ");
+                       }
+               }
+               if (builder.length() > 3)
+                       return builder.substring(0, builder.length() - 4);
+               else
+                       return "";
+       }
+
+       public static String xPathNot(String condition) {
+               if (notEmpty(condition))
+                       return "not(" + condition + ")";
+               else
+                       return "";
+       }
+
+       public static String getFreeTextConstraint(String filter) throws RepositoryException {
+               StringBuilder builder = new StringBuilder();
+               if (notEmpty(filter)) {
+                       String[] strs = filter.trim().split(" ");
+                       for (String token : strs) {
+                               builder.append("jcr:contains(.,'*" + encodeXPathStringValue(token) + "*') and ");
+                       }
+                       return builder.substring(0, builder.length() - 4);
+               }
+               return "";
+       }
+
+       public static String getPropertyContains(String propertyName, String filter) throws RepositoryException {
+               if (notEmpty(filter))
+                       return "jcr:contains(@" + propertyName + ",'*" + encodeXPathStringValue(filter) + "*')";
+               return "";
+       }
+
+       private final static DateFormat jcrRefFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'+02:00'");
+
+       /**
+        * @param propertyName
+        * @param calendar       the reference date
+        * @param lowerOrGreater "<", ">" TODO validate ">="
+        * @return
+        * @throws RepositoryException
+        */
+       public static String getPropertyDateComparaison(String propertyName, Calendar cal, String lowerOrGreater)
+                       throws RepositoryException {
+               if (cal != null) {
+                       String jcrDateStr = jcrRefFormatter.format(cal.getTime());
+
+                       // jcrDateStr = "2015-08-03T05:00:03:000Z";
+                       String result = "@" + propertyName + " " + lowerOrGreater + " xs:dateTime('" + jcrDateStr + "')";
+                       return result;
+               }
+               return "";
+       }
+
+       public static String getPropertyEquals(String propertyName, String value) {
+               if (notEmpty(value))
+                       return "@" + propertyName + "='" + encodeXPathStringValue(value) + "'";
+               return "";
+       }
+
+       public static String encodeXPathStringValue(String propertyValue) {
+               // TODO implement safer mechanism to escape invalid characters
+               // Also check why we have used this regex in ResourceSerrviceImpl l 474
+               // String cleanedKey = key.replaceAll("(?:')", "''");
+               String result = propertyValue.replaceAll("'", "''");
+               return result;
+       }
+
+       public static void andAppend(StringBuilder builder, String condition) {
+               if (notEmpty(condition)) {
+                       builder.append(condition);
+                       builder.append(" and ");
+               }
+       }
+
+       public static void appendOrderByProperties(StringBuilder builder, boolean ascending, String... propertyNames) {
+               if (propertyNames.length > 0) {
+                       builder.append(" order by ");
+                       for (String propName : propertyNames)
+                               builder.append("@").append(propName).append(", ");
+                       builder = builder.delete(builder.length() - 2, builder.length());
+                       if (ascending)
+                               builder.append(" ascending ");
+                       else
+                               builder.append(" descending ");
+               }
+       }
+
+       public static void appendAndPropStringCondition(StringBuilder builder, String propertyName, String filter)
+                       throws RepositoryException {
+               if (notEmpty(filter)) {
+                       andAppend(builder, getPropertyContains(propertyName, filter));
+               }
+       }
+
+       public static void appendAndNotPropStringCondition(StringBuilder builder, String propertyName, String filter)
+                       throws RepositoryException {
+               if (notEmpty(filter)) {
+                       String cond = getPropertyContains(propertyName, filter);
+                       builder.append(xPathNot(cond));
+                       builder.append(" and ");
+               }
+       }
+
+       public static Query createQuery(Session session, String queryString) throws RepositoryException {
+               QueryManager queryManager = session.getWorkspace().getQueryManager();
+               // Localise JCR properties for XPATH
+               queryString = localiseJcrItemNames(queryString);
+               return queryManager.createQuery(queryString, QUERY_XPATH);
+       }
+
+       private final static String NS_JCR = "\\{http://www.jcp.org/jcr/1.0\\}";
+       private final static String NS_NT = "\\{http://www.jcp.org/jcr/nt/1.0\\}";
+       private final static String NS_MIX = "\\{http://www.jcp.org/jcr/mix/1.0\\}";
+
+       /**
+        * Replace the generic namespace with the local "jcr:", "nt:", "mix:" values. It
+        * is a workaround that must be later cleaned
+        */
+       public static String localiseJcrItemNames(String name) {
+               name = name.replaceAll(NS_JCR, "jcr:");
+               name = name.replaceAll(NS_NT, "nt:");
+               name = name.replaceAll(NS_MIX, "mix:");
+               return name;
+       }
+
+       private static boolean notEmpty(String stringToTest) {
+               return !(stringToTest == null || "".equals(stringToTest.trim()));
+       }
+
+       /** Singleton. */
+       private XPathUtils() {
+
+       }
+}