View Javadoc
1   package org.argeo.connect.util;
2   
3   import static org.argeo.connect.util.ConnectJcrUtils.notEmpty;
4   
5   import java.text.DateFormat;
6   import java.text.SimpleDateFormat;
7   import java.util.Calendar;
8   
9   import javax.jcr.RepositoryException;
10  import javax.jcr.Session;
11  import javax.jcr.query.Query;
12  import javax.jcr.query.QueryManager;
13  
14  import org.apache.commons.logging.Log;
15  import org.apache.commons.logging.LogFactory;
16  import org.apache.jackrabbit.util.ISO9075;
17  import org.argeo.connect.ConnectConstants;
18  
19  /** Ease XPath generation for JCR requests */
20  public class XPathUtils {
21  	private final static Log log = LogFactory.getLog(XPathUtils.class);
22  
23  	public static String descendantFrom(String parentPath) {
24  		if (notEmpty(parentPath)) {
25  			if ("/".equals(parentPath))
26  				parentPath = "";
27  			// Hardcoded dependency to Jackrabbit. Remove
28  			String result = "/jcr:root" + ISO9075.encodePath(parentPath);
29  			if (log.isTraceEnabled()) {
30  				String result2 = "/jcr:root" + parentPath;
31  				if (!result2.equals(result))
32  					log.warn("Encoded Path " + result2 + " --> " + result);
33  			}
34  			return result;
35  		} else
36  			return "";
37  	}
38  
39  	public static String localAnd(String... conditions) {
40  		StringBuilder builder = new StringBuilder();
41  		for (String condition : conditions) {
42  			if (notEmpty(condition)) {
43  				builder.append(" ").append(condition).append(" and ");
44  			}
45  		}
46  		if (builder.length() > 3)
47  			return builder.substring(0, builder.length() - 4);
48  		else
49  			return "";
50  	}
51  
52  	public static String xPathNot(String condition) {
53  		if (notEmpty(condition))
54  			return "not(" + condition + ")";
55  		else
56  			return "";
57  	}
58  
59  	public static String getFreeTextConstraint(String filter) throws RepositoryException {
60  		StringBuilder builder = new StringBuilder();
61  		if (notEmpty(filter)) {
62  			String[] strs = filter.trim().split(" ");
63  			for (String token : strs) {
64  				builder.append("jcr:contains(.,'*" + encodeXPathStringValue(token) + "*') and ");
65  			}
66  			return builder.substring(0, builder.length() - 4);
67  		}
68  		return "";
69  	}
70  
71  	public static String getPropertyContains(String propertyName, String filter) throws RepositoryException {
72  		if (notEmpty(filter))
73  			return "jcr:contains(@" + propertyName + ",'*" + encodeXPathStringValue(filter) + "*')";
74  		return "";
75  	}
76  
77  	private final static DateFormat jcrRefFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'+02:00'");
78  
79  	/**
80  	 * @param propertyName
81  	 * @param calendar
82  	 *            the reference date
83  	 * @param lowerOrGreater
84  	 *            "<", ">" TODO validate ">="
85  	 * @return
86  	 * @throws RepositoryException
87  	 */
88  	public static String getPropertyDateComparaison(String propertyName, Calendar cal, String lowerOrGreater)
89  			throws RepositoryException {
90  		if (cal != null) {
91  			String jcrDateStr = jcrRefFormatter.format(cal.getTime());
92  
93  			// jcrDateStr = "2015-08-03T05:00:03:000Z";
94  			String result = "@" + propertyName + " " + lowerOrGreater + " xs:dateTime('" + jcrDateStr + "')";
95  			return result;
96  		}
97  		return "";
98  	}
99  
100 	public static String getPropertyEquals(String propertyName, String value) {
101 		if (notEmpty(value))
102 			return "@" + propertyName + "='" + encodeXPathStringValue(value) + "'";
103 		return "";
104 	}
105 
106 	public static String encodeXPathStringValue(String propertyValue) {
107 		// TODO implement safer mechanism to escape invalid characters
108 		// Also check why we have used this regex in ResourceSerrviceImpl l 474
109 		// String cleanedKey = key.replaceAll("(?:')", "''");
110 		String result = propertyValue.replaceAll("'", "''");
111 		return result;
112 	}
113 
114 	public static void andAppend(StringBuilder builder, String condition) {
115 		if (notEmpty(condition)) {
116 			builder.append(condition);
117 			builder.append(" and ");
118 		}
119 	}
120 
121 	public static void appendOrderByProperties(StringBuilder builder, boolean ascending, String... propertyNames) {
122 		if (propertyNames.length > 0) {
123 			builder.append(" order by ");
124 			for (String propName : propertyNames)
125 				builder.append("@").append(propName).append(", ");
126 			builder = builder.delete(builder.length() - 2, builder.length());
127 			if (ascending)
128 				builder.append(" ascending ");
129 			else
130 				builder.append(" descending ");
131 		}
132 	}
133 
134 	public static void appendAndPropStringCondition(StringBuilder builder, String propertyName, String filter)
135 			throws RepositoryException {
136 		if (notEmpty(filter)) {
137 			andAppend(builder, getPropertyContains(propertyName, filter));
138 		}
139 	}
140 
141 	public static void appendAndNotPropStringCondition(StringBuilder builder, String propertyName, String filter)
142 			throws RepositoryException {
143 		if (notEmpty(filter)) {
144 			String cond = getPropertyContains(propertyName, filter);
145 			builder.append(xPathNot(cond));
146 			builder.append(" and ");
147 		}
148 	}
149 
150 	public static Query createQuery(Session session, String queryString) throws RepositoryException {
151 		QueryManager queryManager = session.getWorkspace().getQueryManager();
152 		// Localise JCR properties for XPATH
153 		queryString = localiseJcrItemNames(queryString);
154 		return queryManager.createQuery(queryString, ConnectConstants.QUERY_XPATH);
155 	}
156 
157 	private final static String NS_JCR = "\\{http://www.jcp.org/jcr/1.0\\}";
158 	private final static String NS_NT = "\\{http://www.jcp.org/jcr/nt/1.0\\}";
159 	private final static String NS_MIX = "\\{http://www.jcp.org/jcr/mix/1.0\\}";
160 
161 	/**
162 	 * Replace the generic namespace with the local "jcr:", "nt:", "mix:"
163 	 * values. It is a workaround that must be later cleaned
164 	 */
165 	public static String localiseJcrItemNames(String name) {
166 		name = name.replaceAll(NS_JCR, "jcr:");
167 		name = name.replaceAll(NS_NT, "nt:");
168 		name = name.replaceAll(NS_MIX, "mix:");
169 		return name;
170 	}
171 }