package org.argeo.suite.util; 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.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.util.ISO9075; /** Ease XPath generation for JCR requests */ public class XPathUtils { private final static Log log = LogFactory.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() { } }