View Javadoc

1   /*
2    * Copyright (c) 2007, James Leigh All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    * 
7    * - Redistributions of source code must retain the above copyright notice, this
8    *   list of conditions and the following disclaimer.
9    * - Redistributions in binary form must reproduce the above copyright notice,
10   *   this list of conditions and the following disclaimer in the documentation
11   *   and/or other materials provided with the distribution. 
12   * - Neither the name of the openrdf.org nor the names of its contributors may
13   *   be used to endorse or promote products derived from this software without
14   *   specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   * 
28   */
29  package org.openrdf.elmo.sesame;
30  
31  import java.io.Closeable;
32  import java.io.IOException;
33  import java.lang.reflect.UndeclaredThrowableException;
34  import java.util.ArrayList;
35  import java.util.Calendar;
36  import java.util.Date;
37  import java.util.GregorianCalendar;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Locale;
41  import java.util.WeakHashMap;
42  
43  import javax.persistence.FlushModeType;
44  import javax.persistence.NoResultException;
45  import javax.persistence.NonUniqueResultException;
46  import javax.persistence.Query;
47  import javax.persistence.TemporalType;
48  import javax.xml.datatype.DatatypeConfigurationException;
49  import javax.xml.datatype.DatatypeConstants;
50  import javax.xml.datatype.DatatypeFactory;
51  import javax.xml.datatype.XMLGregorianCalendar;
52  import javax.xml.namespace.QName;
53  
54  import org.openrdf.elmo.ElmoQuery;
55  import org.openrdf.elmo.exceptions.ElmoConversionException;
56  import org.openrdf.elmo.exceptions.ElmoIOException;
57  import org.openrdf.elmo.sesame.iterators.ElmoIteration;
58  import org.openrdf.model.Value;
59  import org.openrdf.model.ValueFactory;
60  import org.openrdf.query.QueryEvaluationException;
61  import org.openrdf.query.TupleQuery;
62  import org.openrdf.query.TupleQueryResult;
63  import org.openrdf.repository.RepositoryConnection;
64  
65  /**
66   * Implements {@link ElmoQuery} for use with SesameManager.
67   * 
68   * @author James Leigh
69   */
70  public class SesameQuery implements ElmoQuery, Query {
71  
72  	protected SesameManager manager;
73  
74  	protected TupleQuery query;
75  
76  	private WeakHashMap<Closeable, Object> opened;
77  
78  	private int firstResult;
79  
80  	private int maxResults;
81  
82  	public SesameQuery(SesameManager manager, TupleQuery query) {
83  		this.manager = manager;
84  		this.opened = new WeakHashMap<Closeable, Object>(4);
85  		this.query = query;
86  	}
87  
88  	public void close() {
89  		try {
90  			for (Closeable c : opened.keySet()) {
91  				c.close();
92  			}
93  		} catch (IOException e) {
94  			throw new UndeclaredThrowableException(e);
95  		}
96  	}
97  
98  	@SuppressWarnings("unchecked")
99  	protected Iterator evaluateQuery() throws QueryEvaluationException {
100 		Iterator iter;
101 		TupleQueryResult result = query.evaluate();
102 		int max = maxResults <= 0 ? 0 : maxResults + firstResult;
103 		if (result.getBindingNames().size() > 1) {
104 			iter = new ElmoTupleQueryResult(manager, result, max);
105 		} else {
106 			iter = new ElmoSingleQueryResult(manager, result, max);
107 		}
108 		return iter;
109 	}
110 
111 	public Iterator evaluate() {
112 		try {
113 			Iterator result = evaluateQuery();
114 			if (result instanceof Closeable)
115 				opened.put((Closeable) result, Boolean.TRUE);
116 			if (firstResult > 0) {
117 				for (int i = 0; i < firstResult && result.hasNext(); i++) {
118 					result.next();
119 				}
120 			}
121 			return result;
122 		} catch (QueryEvaluationException e) {
123 			throw new ElmoIOException(e);
124 		}
125 	}
126 
127 	public Object getSingleResult() {
128 		Iterator iter = evaluate();
129 		try {
130 			if (!iter.hasNext())
131 				throw new NoResultException("No results");
132 			Object result = iter.next();
133 			if (iter.hasNext())
134 				throw new NonUniqueResultException("More than one result");
135 			return result;
136 		} finally {
137 			ElmoIteration.close(iter);
138 		}
139 	}
140 
141 	public List getResultList() {
142 		List result = new ArrayList();
143 		Iterator iter = evaluate();
144 		try {
145 			for (int count = 0; iter.hasNext(); count++) {
146 				result.add(iter.next());
147 			}
148 			return result;
149 		} finally {
150 			ElmoIteration.close(iter);
151 		}
152 	}
153 
154 	public Iterator iterator() {
155 		return evaluate();
156 	}
157 
158 	public SesameQuery setFirstResult(int startPosition) {
159 		this.firstResult = startPosition;
160 		return this;
161 	}
162 
163 	public SesameQuery setMaxResults(int maxResult) {
164 		this.maxResults = maxResult;
165 		return this;
166 	}
167 
168 	public ElmoQuery setParameter(String name, String label, Locale locale) {
169 		RepositoryConnection conn = manager.getConnection();
170 		ValueFactory vf = conn.getRepository().getValueFactory();
171 		if (locale == null) {
172 			setBinding(name, vf.createLiteral(label));
173 		} else {
174 			String lang = locale.toString().toLowerCase().replace('_', '-');
175 			setBinding(name, vf.createLiteral(label, lang));
176 		}
177 		return this;
178 	}
179 
180 	public SesameQuery setParameter(String name, Object value) {
181 		if (value instanceof Value) {
182 			setBinding(name, (Value) value);
183 		} else if (value instanceof QName) {
184 			setBinding(name, manager.createResource((QName) value));
185 		} else {
186 			setBinding(name, manager.getValue(value));
187 		}
188 		return this;
189 	}
190 
191 	@Override
192 	public String toString() {
193 		if (query == null)
194 			return super.toString();
195 		return query.toString();
196 	}
197 
198 	protected void setBinding(String name, Value value) {
199 		if (query == null)
200 			throw new UnsupportedOperationException();
201 		query.setBinding(name, value);
202 	}
203 
204 	public int executeUpdate() {
205 		throw new UnsupportedOperationException();
206 	}
207 
208 	public SesameQuery setFlushMode(FlushModeType flushMode) {
209 		if (FlushModeType.AUTO.equals(flushMode)) {
210 			manager.flush();
211 		}
212 		return this;
213 	}
214 
215 	public SesameQuery setHint(String hintName, Object value) {
216 		return this;
217 	}
218 
219 	public SesameQuery setParameter(String name, Date value,
220 			TemporalType temporalType) {
221 		int y, M, d, h, m, s, i, z;
222 		try {
223 			z = DatatypeConstants.FIELD_UNDEFINED;
224 			DatatypeFactory factory = DatatypeFactory.newInstance();
225 			XMLGregorianCalendar xcal;
226 			switch (temporalType) {
227 			case DATE:
228 				y = value.getYear();
229 				M = value.getMonth() + 1;
230 				d = value.getDate();
231 				xcal = factory.newXMLGregorianCalendarDate(y, M, d, z);
232 				break;
233 			case TIME:
234 				h = value.getHours();
235 				m = value.getMinutes();
236 				s = value.getSeconds();
237 				i = (int) (value.getTime() % 1000);
238 				xcal = factory.newXMLGregorianCalendarTime(h, m, s, i, z);
239 				break;
240 			case TIMESTAMP:
241 				y = value.getYear();
242 				M = value.getMonth() + 1;
243 				d = value.getDate();
244 				h = value.getHours();
245 				m = value.getMinutes();
246 				s = value.getSeconds();
247 				i = (int) (value.getTime() % 1000);
248 				xcal = factory.newXMLGregorianCalendar(y, M, d, h, m, s, i, z);
249 				break;
250 			default:
251 				throw new AssertionError();
252 			}
253 			return setParameter(name, xcal);
254 		} catch (DatatypeConfigurationException e) {
255 			throw new ElmoConversionException(e);
256 		}
257 	}
258 
259 	public SesameQuery setParameter(String name, Calendar value,
260 			TemporalType temporalType) {
261 		assert value instanceof GregorianCalendar : value;
262 		GregorianCalendar cal = (GregorianCalendar) value;
263 		try {
264 			DatatypeFactory factory = DatatypeFactory.newInstance();
265 			XMLGregorianCalendar xcal = factory.newXMLGregorianCalendar(cal);
266 			switch (temporalType) {
267 			case DATE:
268 				xcal.setHour(DatatypeConstants.FIELD_UNDEFINED);
269 				xcal.setMinute(DatatypeConstants.FIELD_UNDEFINED);
270 				xcal.setSecond(DatatypeConstants.FIELD_UNDEFINED);
271 				xcal.setMillisecond(DatatypeConstants.FIELD_UNDEFINED);
272 				break;
273 			case TIME:
274 				xcal.setYear(DatatypeConstants.FIELD_UNDEFINED);
275 				xcal.setMonth(DatatypeConstants.FIELD_UNDEFINED);
276 				xcal.setDay(DatatypeConstants.FIELD_UNDEFINED);
277 				break;
278 			case TIMESTAMP:
279 				break;
280 			}
281 			return setParameter(name, xcal);
282 		} catch (DatatypeConfigurationException e) {
283 			throw new ElmoConversionException(e);
284 		}
285 	}
286 
287 	public SesameQuery setParameter(int arg0, Object arg1) {
288 		throw new UnsupportedOperationException(
289 				"Parameters by index are not supported");
290 	}
291 
292 	public SesameQuery setParameter(int arg0, Date arg1, TemporalType arg2) {
293 		throw new UnsupportedOperationException(
294 				"Parameters by index are not supported");
295 	}
296 
297 	public SesameQuery setParameter(int arg0, Calendar arg1, TemporalType arg2) {
298 		throw new UnsupportedOperationException(
299 				"Parameters by index are not supported");
300 	}
301 
302 }