001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.jdbc.metadata; 017 018import java.sql.ResultSetMetaData; 019import java.sql.SQLException; 020import java.util.Iterator; 021import java.util.List; 022import java.util.NoSuchElementException; 023import javax.jcr.ItemNotFoundException; 024import javax.jcr.Node; 025import javax.jcr.NodeIterator; 026import javax.jcr.RepositoryException; 027import javax.jcr.Value; 028import javax.jcr.query.QueryResult; 029import javax.jcr.query.Row; 030import javax.jcr.query.RowIterator; 031import org.modeshape.jdbc.JdbcJcrValueFactory; 032 033/** 034 * The MetaDataQueryResult is used to provide {@link NodeIterator} and the {@link RowIterator} in order to provide query results 035 * when metadata is requested. This is done because there are no sql queries that can be executed to obtain certain types of 036 * metadata in a return {@link QueryResult} from the ModeShapeEngine. 037 */ 038public class MetaDataQueryResult implements javax.jcr.query.QueryResult { 039 040 private ResultSetMetaData rsmd; 041 private String[] columnNames = null; 042 private List<List<?>> tuplesArray = null; 043 044 public static MetaDataQueryResult createResultSet( List<List<?>> records, 045 ResultSetMetaData resultSetMetaData ) throws SQLException { 046 try { 047 return new MetaDataQueryResult(records, resultSetMetaData); 048 } catch (RepositoryException e) { 049 if (e.getCause() instanceof SQLException) throw (SQLException)e.getCause(); 050 throw new SQLException(e); 051 } 052 } 053 054 MetaDataQueryResult( List<List<?>> tuples, 055 ResultSetMetaData rsmd ) throws RepositoryException { 056 this.rsmd = rsmd; 057 this.tuplesArray = tuples; 058 getColumnNames(); 059 } 060 061 @Override 062 public String[] getColumnNames() throws RepositoryException { 063 if (columnNames != null) { 064 return columnNames; 065 } 066 067 try { 068 columnNames = new String[rsmd.getColumnCount()]; 069 for (int col = 0; col < columnNames.length; col++) { 070 columnNames[col] = rsmd.getColumnName(col + 1); 071 } 072 073 return columnNames; 074 } catch (SQLException sqle) { 075 throw new RepositoryException(sqle); 076 } 077 } 078 079 @Override 080 public NodeIterator getNodes() { 081 throw new UnsupportedOperationException(); 082 } 083 084 @Override 085 public RowIterator getRows() { 086 return new QueryResultRowIterator(tuplesArray, columnNames); 087 } 088 089 public String[] getColumnTypes() { 090 String[] columnTypes = new String[columnNames.length]; 091 for (int i = 0; i <= columnNames.length; i++) { 092 try { 093 columnTypes[i] = rsmd.getColumnTypeName(i + 1); 094 } catch (SQLException e) { 095 columnTypes[i] = "NotFound"; 096 } 097 } 098 return columnTypes; 099 } 100 101 @Override 102 public String[] getSelectorNames() { 103 throw new UnsupportedOperationException(); 104 } 105 106} 107 108class QueryResultRowIterator implements RowIterator { 109 private final Iterator<List<?>> tuples; 110 private long position = 0L; 111 private long numRows; 112 private Row nextRow; 113 private String[] colNames; 114 115 protected QueryResultRowIterator( List<List<?>> tuplesArray, 116 String[] columnNames ) { 117 this.tuples = tuplesArray.iterator(); 118 this.numRows = tuplesArray.size(); 119 this.colNames = columnNames; 120 } 121 122 public boolean hasSelector( String selectorName ) { 123 return false; 124 } 125 126 /** 127 * {@inheritDoc} 128 * 129 * @see javax.jcr.query.RowIterator#nextRow() 130 */ 131 @Override 132 public Row nextRow() { 133 if (nextRow == null) { 134 // Didn't call 'hasNext()' ... 135 if (!hasNext()) { 136 throw new NoSuchElementException(); 137 } 138 } 139 assert nextRow != null; 140 Row result = nextRow; 141 nextRow = null; 142 position++; 143 return result; 144 } 145 146 @Override 147 public long getPosition() { 148 return position; 149 } 150 151 @Override 152 public long getSize() { 153 return numRows; 154 } 155 156 @Override 157 public void skip( long skipNum ) { 158 for (long i = 0L; i != skipNum; ++i) { 159 tuples.next(); 160 } 161 position += skipNum; 162 } 163 164 @Override 165 public boolean hasNext() { 166 if (nextRow != null) { 167 return true; 168 } 169 170 while (tuples.hasNext()) { 171 final List<?> tuple = tuples.next(); 172 // Get the next row ... 173 nextRow = getNextRow(tuple); 174 if (nextRow != null) return true; 175 --numRows; 176 } 177 return false; 178 } 179 180 private Row getNextRow( List<?> tuple ) { 181 return new QueryResultRow(this, tuple, colNames); 182 } 183 184 @Override 185 public Object next() { 186 return nextRow(); 187 } 188 189 @Override 190 public void remove() { 191 throw new UnsupportedOperationException(); 192 } 193} 194 195class QueryResultRow implements Row { 196 protected final QueryResultRowIterator iterator; 197 protected final List<?> tuple; 198 private String[] columnNames = null; 199 200 protected QueryResultRow( QueryResultRowIterator iterator, 201 List<?> tuple, 202 String[] colNames ) { 203 this.iterator = iterator; 204 this.tuple = tuple; 205 this.columnNames = colNames; 206 } 207 208 @Override 209 public Node getNode() { 210 throw new UnsupportedOperationException(); 211 } 212 213 @Override 214 public Node getNode( String selectorName ) { 215 throw new UnsupportedOperationException(); 216 } 217 218 @Override 219 public String getPath() { 220 throw new UnsupportedOperationException(); 221 } 222 223 @Override 224 public String getPath( String selectorName ) { 225 throw new UnsupportedOperationException(); 226 } 227 228 @Override 229 public double getScore() /* throws RepositoryException */{ 230 throw new UnsupportedOperationException(); 231 } 232 233 @Override 234 public double getScore( String selectorName ) /* throws RepositoryException */{ 235 throw new UnsupportedOperationException(); 236 } 237 238 @Override 239 public Value getValue( String arg0 ) throws ItemNotFoundException { 240 int pos = getColumnPosition(arg0); 241 if (pos >= 0) { 242 return createValue(tuple.get(pos)); 243 } 244 245 throw new ItemNotFoundException("Item " + arg0 + " not found"); 246 } 247 248 private int getColumnPosition( String colName ) { 249 for (int i = 0; i < columnNames.length; i++) { 250 if (columnNames[i].equals(colName)) return i; 251 } 252 return -1; 253 254 } 255 256 @SuppressWarnings( "unused" ) 257 @Override 258 public Value[] getValues() throws RepositoryException { 259 Value[] values = new Value[tuple.size()]; 260 for (int i = 0; i < values.length; i++) { 261 values[i] = createValue(tuple.get(i)); 262 263 } 264 return values; 265 } 266 267 private Value createValue( final Object value ) { 268 return JdbcJcrValueFactory.createValue(value); 269 } 270 271}