| /* |
| |
| Derby - Class org.apache.derby.impl.sql.GenericResultDescription |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.sql; |
| |
| import org.apache.derby.iapi.sql.ResultColumnDescriptor; |
| import org.apache.derby.iapi.sql.ResultDescription; |
| |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| |
| import org.apache.derby.iapi.services.io.ArrayUtil; |
| import org.apache.derby.iapi.services.io.StoredFormatIds; |
| import org.apache.derby.iapi.services.io.Formatable; |
| import org.apache.derby.iapi.util.StringUtil; |
| |
| import java.io.ObjectOutput; |
| import java.io.ObjectInput; |
| import java.io.IOException; |
| import java.sql.ResultSetMetaData; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * GenericResultDescription: basic implementation of result |
| * description, used in conjunction with the other |
| * implementations in this package. This implementation |
| * of ResultDescription may be used by anyone. |
| * |
| */ |
| public final class GenericResultDescription |
| implements ResultDescription, Formatable |
| { |
| |
| /******************************************************** |
| ** |
| ** This class implements Formatable. That means that it |
| ** can write itself to and from a formatted stream. If |
| ** you add more fields to this class, make sure that you |
| ** also write/read them with the writeExternal()/readExternal() |
| ** methods. |
| ** |
| ** If, inbetween releases, you add more fields to this class, |
| ** then you should bump the version number emitted by the getTypeFormatId() |
| ** method. |
| ** |
| ********************************************************/ |
| |
| private ResultColumnDescriptor[] columns; |
| private String statementType; |
| |
| /** |
| * Saved JDBC ResultSetMetaData object. |
| * @see ResultDescription#setMetaData(java.sql.ResultSetMetaData) |
| */ |
| private transient ResultSetMetaData metaData; |
| |
| /** |
| * A map which maps a column name to a column number. |
| * Entries only added when accessing columns with the name. |
| */ |
| private Map<String,Integer> columnNameMap; |
| |
| /** |
| * Niladic constructor for Formatable |
| */ |
| public GenericResultDescription() |
| { |
| } |
| |
| /** |
| * Build a GenericResultDescription from columns and type |
| * |
| * @param columns an array of col descriptors |
| * @param statementType the type |
| */ |
| public GenericResultDescription(ResultColumnDescriptor[] columns, |
| String statementType) |
| { |
| this.columns = ArrayUtil.copy(columns); |
| this.statementType = statementType; |
| } |
| |
| /** |
| * Build a GenericResultDescription |
| * |
| * @param rd the result description |
| * @param theCols the columns to take from the input rd |
| */ |
| public GenericResultDescription |
| ( |
| ResultDescription rd, |
| int[] theCols |
| ) |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(theCols != null, "theCols argument to GenericResultDescription is null"); |
| } |
| |
| this.columns = new ResultColumnDescriptor[theCols.length]; |
| for (int i = 0; i < theCols.length; i++) |
| { |
| columns[i] = rd.getColumnDescriptor(theCols[i]); |
| } |
| this.statementType = rd.getStatementType(); |
| } |
| |
| // |
| // ResultDescription interface |
| // |
| /** |
| * @see ResultDescription#getStatementType |
| */ |
| public String getStatementType() { |
| return statementType; |
| } |
| |
| /** |
| * @see ResultDescription#getColumnCount |
| */ |
| public int getColumnCount() |
| { |
| return (columns == null) ? 0 : columns.length; |
| } |
| |
| public ResultColumnDescriptor[] getColumnInfo() { |
| return ArrayUtil.copy(columns); |
| } |
| |
| public ResultColumnDescriptor getColumnInfo( int idx ) { return columns[ idx ]; } |
| |
| /** |
| * position is 1-based. |
| * @see ResultDescription#getColumnDescriptor |
| */ |
| public ResultColumnDescriptor getColumnDescriptor(int position) { |
| return columns[position-1]; |
| } |
| |
| /** |
| * Get a new result description that has been truncated |
| * from input column number. If the input column is |
| * 5, then columns 5 to getColumnCount() are removed. |
| * The new ResultDescription points to the same |
| * ColumnDescriptors (this method performs a shallow |
| * copy. |
| * |
| * @param truncateFrom the starting column to remove |
| * |
| * @return a new ResultDescription |
| */ |
| public ResultDescription truncateColumns(int truncateFrom) |
| { |
| if (SanityManager.DEBUG) |
| { |
| if (!(truncateFrom > 0 && columns != null)) |
| { |
| SanityManager.THROWASSERT("bad truncate value: "+truncateFrom+" is too low"); |
| } |
| if (truncateFrom > columns.length) |
| { |
| SanityManager.THROWASSERT("bad truncate value: "+truncateFrom+" is too high"); |
| } |
| } |
| ResultColumnDescriptor[] newColumns = new ResultColumnDescriptor[truncateFrom-1]; |
| System.arraycopy(columns, 0, newColumns, 0, newColumns.length); |
| return new GenericResultDescription(newColumns, statementType); |
| } |
| |
| |
| ////////////////////////////////////////////// |
| // |
| // FORMATABLE |
| // |
| ////////////////////////////////////////////// |
| /** |
| * Write this object out |
| * |
| * @param out write bytes here |
| * |
| * @exception IOException thrown on error |
| */ |
| public void writeExternal(ObjectOutput out) throws IOException |
| { |
| int len = (columns == null) ? 0 : columns.length; |
| |
| out.writeObject(statementType); |
| out.writeInt(len); |
| while(len-- > 0) |
| { |
| /* |
| ** If we don't have a GenericColumnsDescriptor, |
| ** create one now and use that to write out. |
| ** Do this to avoid writing out query tree |
| ** implementations of ResultColumnDescriptor |
| */ |
| if (!(columns[len] instanceof |
| GenericColumnDescriptor)) |
| { |
| columns[len] = new GenericColumnDescriptor(columns[len]); |
| } |
| out.writeObject(columns[len]); |
| } |
| } |
| |
| /** |
| * Read this object from a stream of stored objects. |
| * |
| * @param in read this. |
| * |
| * @exception IOException thrown on error |
| * @exception ClassNotFoundException thrown on error |
| */ |
| public void readExternal(ObjectInput in) |
| throws IOException, ClassNotFoundException |
| { |
| int len; |
| |
| columns = null; |
| statementType = (String)in.readObject(); |
| len = in.readInt(); |
| if (len > 0) |
| { |
| columns = new GenericColumnDescriptor[len]; |
| while(len-- > 0) |
| { |
| columns[len] = (ResultColumnDescriptor)in.readObject(); |
| } |
| } |
| } |
| |
| /** |
| * Get the formatID which corresponds to this class. |
| * |
| * @return the formatID of this class |
| */ |
| public int getTypeFormatId() { return StoredFormatIds.GENERIC_RESULT_DESCRIPTION_V01_ID; } |
| |
| |
| |
| public String toString() |
| { |
| if (SanityManager.DEBUG) |
| { |
| StringBuffer colStr = new StringBuffer(); |
| for (int i = 0; i < columns.length; i++) |
| { |
| colStr.append("column["+i+"]\n"); |
| colStr.append(columns[i].toString()); |
| } |
| return "GenericResultDescription\n" + |
| "\tStatementType = "+statementType+"\n" + |
| "\tCOLUMNS\n" + colStr.toString(); |
| } |
| else |
| { |
| return ""; |
| } |
| } |
| |
| /** |
| * Set the meta data if it has not already been set. |
| */ |
| public synchronized void setMetaData(ResultSetMetaData rsmd) { |
| if (metaData == null) |
| metaData = rsmd; |
| } |
| |
| /** |
| * Get the saved meta data. |
| */ |
| public synchronized ResultSetMetaData getMetaData() { |
| return metaData; |
| } |
| |
| /** |
| * Find a column name based upon the JDBC rules for |
| * getXXX and setXXX. Name matching is case-insensitive, |
| * matching the first name (1-based) if there are multiple |
| * columns that map to the same name. |
| */ |
| public int findColumnInsenstive(String columnName) { |
| |
| final Map<String,Integer> workMap; |
| |
| synchronized (this) { |
| if (columnNameMap==null) { |
| // updateXXX and getXXX methods are case insensitive and the |
| // first column should be returned. The loop goes backward to |
| // create a map which preserves this property. |
| Map<String,Integer> map = new HashMap<String,Integer>(); |
| for (int i = getColumnCount(); i>=1; i--) { |
| |
| final String key = StringUtil. |
| SQLToUpperCase( |
| getColumnDescriptor(i).getName()); |
| |
| final Integer value = i; |
| |
| map.put(key, value); |
| } |
| |
| // Ensure this map can never change. |
| columnNameMap = Collections.unmodifiableMap(map); |
| } |
| workMap = columnNameMap; |
| } |
| |
| Integer val = (Integer) workMap.get(columnName); |
| if (val==null) { |
| val = (Integer) workMap.get(StringUtil.SQLToUpperCase(columnName)); |
| } |
| if (val==null) { |
| return -1; |
| } else { |
| return val.intValue(); |
| } |
| } |
| } |
| |