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}