View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jdbc.metadata;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.math.BigDecimal;
30  import java.sql.ResultSetMetaData;
31  import java.sql.SQLException;
32  import java.util.Calendar;
33  import java.util.Date;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.NoSuchElementException;
37  
38  import javax.jcr.Binary;
39  import javax.jcr.ItemNotFoundException;
40  import javax.jcr.Node;
41  import javax.jcr.NodeIterator;
42  import javax.jcr.RepositoryException;
43  import javax.jcr.Value;
44  import javax.jcr.ValueFormatException;
45  import javax.jcr.query.Row;
46  import javax.jcr.query.RowIterator;
47  
48  /**
49   * The MetaDataQueryResult is used to provide {@link NodeIterator} and the
50   * {@link RowIterator} in order to provide query results when metadata is
51   * requested. This is done because there are no sql queries that can be executed
52   * to obtain certain types of metadata in a return QueryResult from the
53   * JcrEngine.
54   */
55  public class MetaDataQueryResult implements javax.jcr.query.QueryResult {
56  
57  	private ResultSetMetaData rsmd;
58  	private String[] columnNames = null;
59  	private List<List<?>> tuplesArray = null;
60  	
61  	
62  	public static  MetaDataQueryResult createResultSet(List<List<?>> records, ResultSetMetaData resultSetMetaData)  {
63  		 MetaDataQueryResult mdqr = new MetaDataQueryResult(records, resultSetMetaData);
64  		 return mdqr;
65      }
66  
67  
68  	MetaDataQueryResult(List<List<?>> tuples,  ResultSetMetaData rsmd) {
69  			this.rsmd = rsmd;
70  			this.tuplesArray = tuples;
71  			getColumnNames();
72  		}
73  
74  	@Override
75  	public String[] getColumnNames()  {
76  		if (columnNames != null) {
77  			return columnNames;
78  		}
79  		
80  		try {
81  			columnNames = new String[rsmd.getColumnCount()];
82  			for (int col = 0; col < columnNames.length; col++) {
83  					columnNames[col] = rsmd.getColumnName(col + 1);
84  			}
85  			
86  			return columnNames;
87  		} catch (SQLException sqle) {
88  			sqle.printStackTrace();
89  			
90  		}
91  		return null;
92  	}
93  
94  	@Override
95  	public NodeIterator getNodes() {
96  		throw new UnsupportedOperationException();
97  	}
98  
99  	@Override
100 	public RowIterator getRows() {
101 		RowIterator ri = new QueryResultRowIterator(tuplesArray, columnNames);
102 		return ri;
103 	}
104 
105 	public String[] getColumnTypes() {
106 		String[] columnTypes = new String[columnNames.length];
107 		for (int i = 0; i <= columnNames.length; i++) {
108 			try {
109 				columnTypes[i] =  rsmd.getColumnTypeName(i + 1);
110 			} catch (SQLException e) {
111 				columnTypes[i] = "NotFound";
112 			}
113 		}
114 		return columnTypes;
115 	}
116 
117 	@Override
118 	public String[] getSelectorNames() {
119 		throw new UnsupportedOperationException();
120 	}
121 
122 }
123 
124 
125 /**
126  */
127 class QueryResultNodeIterator implements NodeIterator {
128 	private final Node[] nodes;
129 	private final int size;
130 	private long position = 0L;
131 
132 	protected QueryResultNodeIterator(Node[] nodes) {
133 		this.nodes = nodes;
134 		this.size = nodes.length;
135 	}
136 
137 	/**
138 	 * {@inheritDoc}
139 	 * 
140 	 * @see javax.jcr.NodeIterator#nextNode()
141 	 */
142 	public Node nextNode() {
143 		Node node = nodes[(int) (position)];
144 		++position;
145 		return node;
146 	}
147 
148 	/**
149 	 * {@inheritDoc}
150 	 * 
151 	 * @see javax.jcr.RangeIterator#getPosition()
152 	 */
153 	public long getPosition() {
154 		return position;
155 	}
156 
157 	/**
158 	 * {@inheritDoc}
159 	 * 
160 	 * @see javax.jcr.RangeIterator#getSize()
161 	 */
162 	public long getSize() {
163 		return size;
164 	}
165 
166 	/**
167 	 * {@inheritDoc}
168 	 * 
169 	 * @see javax.jcr.RangeIterator#skip(long)
170 	 */
171 	public void skip(long skipNum) {
172 		for (long i = 0L; i != skipNum; ++i)
173 			nextNode();
174 	}
175 
176 	/**
177 	 * {@inheritDoc}
178 	 * 
179 	 * @see java.util.Iterator#hasNext()
180 	 */
181 	public boolean hasNext() {
182 		return (nodes.length > position);
183 	}
184 
185 	/**
186 	 * {@inheritDoc}
187 	 * 
188 	 * @see java.util.Iterator#next()
189 	 */
190 	public Object next() {
191 		return nextNode();
192 	}
193 
194 	/**
195 	 * {@inheritDoc}
196 	 * 
197 	 * @see java.util.Iterator#remove()
198 	 */
199 	public void remove() {
200 		throw new UnsupportedOperationException();
201 	}
202 }
203 
204 class QueryResultRowIterator implements RowIterator {
205 	private final Iterator<List<?>> tuples;
206 	private long position = 0L;
207 	private long numRows;
208 	private Row nextRow;
209 	private String[] colNames;
210 
211 	protected QueryResultRowIterator( List<List<?>> tuplesArray,
212 			String[] columnNames) {
213 		this.tuples = tuplesArray.iterator();
214 		this.numRows = tuplesArray.size();
215 		this.colNames = columnNames;
216 	}
217 
218 	public boolean hasSelector(String selectorName) {
219 		return false;
220 	}
221 
222 	/**
223 	 * {@inheritDoc}
224 	 * 
225 	 * @see javax.jcr.query.RowIterator#nextRow()
226 	 */
227 	public Row nextRow() {
228 		if (nextRow == null) {
229 			// Didn't call 'hasNext()' ...
230 			if (!hasNext()) {
231 				throw new NoSuchElementException();
232 			}
233 		}
234 		assert nextRow != null;
235 		Row result = nextRow;
236 		nextRow = null;
237 		position++;
238 		return result;
239 	}
240 
241 	/**
242 	 * {@inheritDoc}
243 	 * 
244 	 * @see javax.jcr.RangeIterator#getPosition()
245 	 */
246 	public long getPosition() {
247 		return position;
248 	}
249 
250 	/**
251 	 * {@inheritDoc}
252 	 * 
253 	 * @see javax.jcr.RangeIterator#getSize()
254 	 */
255 	public long getSize() {
256 		return numRows;
257 	}
258 
259 	/**
260 	 * {@inheritDoc}
261 	 * 
262 	 * @see javax.jcr.RangeIterator#skip(long)
263 	 */
264 	public void skip(long skipNum) {
265 		for (long i = 0L; i != skipNum; ++i) {
266 			tuples.next();
267 		}
268 		position += skipNum;
269 	}
270 
271 	/**
272 	 * {@inheritDoc}
273 	 * 
274 	 * @see java.util.Iterator#hasNext()
275 	 */
276 	public boolean hasNext() {
277 		if (nextRow != null) {
278 			return true;
279 		}
280 
281 		while (tuples.hasNext()) {
282 			final List<?> tuple = tuples.next();
283 			try {
284 				// Get the next row ...
285 				nextRow = getNextRow(tuple);
286 				if (nextRow != null)
287 					return true;
288 			} catch (RepositoryException e) {
289 				// The node could not be found in this session, so skip it ...
290 			}
291 			--numRows;
292 		}
293 		return false;
294 	}
295 
296 	/**
297 	 * @param tuple
298 	 * @return Row
299 	 * @throws RepositoryException
300 	 */
301 	private Row getNextRow(List<?> tuple) throws RepositoryException {
302 		return new QueryResultRow(this, tuple, colNames);
303 	}
304 
305 	/**
306 	 * {@inheritDoc}
307 	 * 
308 	 * @see java.util.Iterator#next()
309 	 */
310 	public Object next() {
311 		return nextRow();
312 	}
313 
314 	/**
315 	 * {@inheritDoc}
316 	 * 
317 	 * @see java.util.Iterator#remove()
318 	 */
319 	public void remove() {
320 		throw new UnsupportedOperationException();
321 	}
322 }
323 
324 class QueryResultRow implements Row {
325 	protected final QueryResultRowIterator iterator;
326 	protected final List<?> tuple;
327 	private String[] columnNames = null;
328 
329 	protected QueryResultRow(QueryResultRowIterator iterator,
330 			List<?> tuple, String[] colNames) {
331 		this.iterator = iterator;
332 		this.tuple = tuple;
333 		this.columnNames = colNames;
334 	}
335 
336 	/**
337 	 * {@inheritDoc}
338 	 * 
339 	 * @see javax.jcr.query.Row#getNode()
340 	 */
341 	@Override
342 	public Node getNode() throws RepositoryException {
343 		throw new UnsupportedOperationException();
344 	}
345 
346 	@Override
347 	public Node getNode(String selectorName) throws RepositoryException {
348 		throw new UnsupportedOperationException();
349 	}
350 
351 	/**
352 	 * {@inheritDoc}
353 	 * 
354 	 * @see javax.jcr.query.Row#getPath()
355 	 */
356 	@Override
357 	public String getPath() throws RepositoryException {
358 		throw new UnsupportedOperationException();
359 	}
360 
361 	/**
362 	 * {@inheritDoc}
363 	 * 
364 	 * @see javax.jcr.query.Row#getPath(java.lang.String)
365 	 */
366 	@Override
367 	public String getPath(String selectorName) throws RepositoryException {
368 		throw new UnsupportedOperationException();
369 	}
370 
371 	/**
372 	 * {@inheritDoc}
373 	 * 
374 	 * @see javax.jcr.query.Row#getScore()
375 	 */
376 	@Override
377 	public double getScore() /* throws RepositoryException */{
378 		throw new UnsupportedOperationException();
379 	}
380 
381 	/**
382 	 * {@inheritDoc}
383 	 * 
384 	 * @see javax.jcr.query.Row#getScore(java.lang.String)
385 	 */
386 	@Override
387 	public double getScore(String selectorName) /* throws RepositoryException */{
388 		throw new UnsupportedOperationException();
389 	}
390 
391 	/**
392 	 * @throws ItemNotFoundException
393 	 */
394 	@Override
395 	public Value getValue(String arg0) throws ItemNotFoundException {
396 		int pos = getColumnPosition(arg0);
397 		if (pos >= 0) {
398 			return createValue(tuple.get(pos));
399 		}
400 
401 		throw new ItemNotFoundException("Item " + arg0 + " not found");
402 	}
403 
404 	private int getColumnPosition(String colName) {
405 		for (int i = 0; i < columnNames.length; i++) {
406 			if (columnNames[i].equals(colName))
407 				return i;
408 		}
409 		return -1;
410 
411 	}
412 
413 	/**
414 	 * @throws RepositoryException
415 	 */
416 	@Override
417 	public Value[] getValues() throws RepositoryException {
418 		Value[] values = new Value[tuple.size()];
419 		for (int i = 0; i < values.length; i++) {
420 			values[i] = createValue(tuple.get(i));
421 
422 		}
423 		return values;
424 	}
425 
426 	private Value createValue(final Object value) {
427 
428 		if (value == null)
429 			return null;
430 
431 		Value rtnvalue = new Value() {
432 			final Object valueObject = value;
433 
434 			@Override
435 			public boolean getBoolean() throws ValueFormatException,
436 					IllegalStateException, RepositoryException {
437 				if (value instanceof Boolean) {
438 					return ((Boolean) valueObject).booleanValue();
439 				}
440 				throw new ValueFormatException("Value not a Boolean");
441 			}
442 
443 			@Override
444 			public Calendar getDate() throws ValueFormatException,
445 					IllegalStateException, RepositoryException {
446 				if (value instanceof Date) {
447 					Calendar c = Calendar.getInstance();
448 					c.setTime((Date) value);
449 
450 					return c;
451 				}
452 				throw new ValueFormatException("Value not instance of Date");
453 			}
454 
455 			@Override
456 			public double getDouble() throws ValueFormatException,
457 					IllegalStateException, RepositoryException {
458 				if (value instanceof Double) {
459 					return ((Double) valueObject).doubleValue();
460 				}
461 
462 				throw new ValueFormatException("Value not a Double");
463 			}
464 
465 			@Override
466 			public long getLong() throws ValueFormatException,
467 					IllegalStateException, RepositoryException {
468 				if (value instanceof Long) {
469 					return ((Long) valueObject).longValue();
470 				}
471 				throw new ValueFormatException("Value not a Long");
472 			}
473 
474 			/**
475 			 * {@inheritDoc}
476 			 * 
477 			 * @see javax.jcr.Value#getBinary()
478 			 */
479 			@Override
480 			public Binary getBinary() throws RepositoryException {
481 				if (value instanceof Binary) {
482 					return ((Binary) valueObject);
483 				}
484 				if (value instanceof byte[]) {
485 					final byte[] bytes = (byte[]) value;
486 					return new Binary() {
487 
488 						@Override
489 						public void dispose() {
490 						}
491 
492 						@Override
493 						public long getSize() {
494 							return bytes.length;
495 						}
496 
497 						@Override
498 						public InputStream getStream() {
499 							return new ByteArrayInputStream(bytes);
500 						}
501 
502 						@Override
503 						public int read(byte[] b, long position)
504 								throws IOException {
505 							if (getSize() <= position)
506 								return -1;
507 							InputStream stream = null;
508 							IOException error = null;
509 							try {
510 								stream = getStream();
511 								// Read/skip the next 'position' bytes ...
512 								long skip = position;
513 								while (skip > 0) {
514 									long skipped = stream.skip(skip);
515 									if (skipped <= 0)
516 										return -1;
517 									skip -= skipped;
518 								}
519 								return stream.read(b);
520 							} catch (IOException e) {
521 								error = e;
522 								throw e;
523 							} finally {
524 								if (stream != null) {
525 									try {
526 										stream.close();
527 									} catch (RuntimeException t) {
528 										// Only throw if we've not already
529 										// thrown an exception ...
530 										if (error == null)
531 											throw t;
532 									} catch (IOException t) {
533 										// Only throw if we've not already
534 										// thrown an exception ...
535 										if (error == null)
536 											throw t;
537 									}
538 								}
539 							}
540 						}
541 
542 					};
543 				}
544 				throw new ValueFormatException("Value not a Binary");
545 			}
546 
547 			/**
548 			 * {@inheritDoc}
549 			 * 
550 			 * @see javax.jcr.Value#getDecimal()
551 			 */
552 			@Override
553 			public BigDecimal getDecimal() throws ValueFormatException,
554 					RepositoryException {
555 				if (value instanceof BigDecimal) {
556 					return ((BigDecimal) valueObject);
557 				}
558 				throw new ValueFormatException("Value not a Decimal");
559 			}
560 
561 			@Override
562 			public InputStream getStream() throws IllegalStateException,
563 					RepositoryException {
564 				if (value instanceof Binary) {
565 					return ((Binary) valueObject).getStream();
566 				}
567 				if (value instanceof InputStream) {
568 					return ((InputStream) valueObject);
569 				}
570 				throw new ValueFormatException("Value not an InputStream");
571 			}
572 
573 			@Override
574 			public String getString() throws IllegalStateException {
575 				if (value instanceof String) {
576 					return (String) valueObject;
577 				}
578 				return valueObject.toString();
579 			}
580 
581 			@Override
582 			public int getType() {
583 				return 1;
584 			}
585 
586 		};
587 
588 		return rtnvalue;
589 
590 	}
591 	
592 }
593