/* * 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 groovy.sql; import groovy.lang.GroovyObjectSupport; import groovy.lang.MissingPropertyException; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Represents an extent of objects. * It's primarily used by methods of Groovy's {@link groovy.sql.Sql} class to return {@code ResultSet} data in map * form; allowing access to the result of a SQL query by the name of the column, or by the column number. * * @author Jean-Louis Berliet */ public class GroovyRowResult extends GroovyObjectSupport implements Map { private final Map result; public GroovyRowResult(Map result) { this.result = result; } /** * Retrieve the value of the property by its (case-insensitive) name. * * @param property is the name of the property to look at * @return the value of the property */ public Object getProperty(String property) { try { Object key = lookupKeyIgnoringCase(property); if (key != null) { return result.get(key); } throw new MissingPropertyException(property, GroovyRowResult.class); } catch (Exception e) { throw new MissingPropertyException(property, GroovyRowResult.class, e); } } private Object lookupKeyIgnoringCase(Object key) { // try some special cases first for efficiency if (result.containsKey(key)) return key; if (!(key instanceof CharSequence)) return null; String keyStr = key.toString(); for (Object next : result.keySet()) { if (!(next instanceof String)) continue; if (keyStr.equalsIgnoreCase((String)next)) return next; } return null; } /** * Retrieve the value of the property by its index. * A negative index will count backwards from the last column. * * @param index is the number of the column to look at * @return the value of the property */ public Object getAt(int index) { try { // a negative index will count backwards from the last column. if (index < 0) index += result.size(); Iterator it = result.values().iterator(); int i = 0; Object obj = null; while ((obj == null) && (it.hasNext())) { if (i == index) obj = it.next(); else it.next(); i++; } return obj; } catch (Exception e) { throw new MissingPropertyException(Integer.toString(index), GroovyRowResult.class, e); } } public String toString() { return result.toString(); } /* * The following methods are needed for implementing the Map interface. * They are mostly delegating the request to the provided Map. */ public void clear() { result.clear(); } /** * Checks if the result contains (ignoring case) the given key. * * @param key the property name to look for * @return true if the result contains this property name */ public boolean containsKey(Object key) { return lookupKeyIgnoringCase(key) != null; } public boolean containsValue(Object value) { return result.containsValue(value); } public Set<Map.Entry> entrySet() { return result.entrySet(); } public boolean equals(Object o) { return result.equals(o); } /** * Find the property value for the given name (ignoring case). * * @param property the name of the property to get * @return the property value */ public Object get(Object property) { if (property instanceof String) return getProperty((String)property); return null; } public int hashCode() { return result.hashCode(); } public boolean isEmpty() { return result.isEmpty(); } public Set keySet() { return result.keySet(); } /** * Associates the specified value with the specified property name in this result. * * @param key the property name for the result * @param value the property value for the result * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ @SuppressWarnings("unchecked") public Object put(Object key, Object value) { // avoid different case keys being added by explicit remove Object orig = remove(key); result.put(key, value); return orig; } /** * Copies all of the mappings from the specified map to this result. * If the map contains different case versions of the same (case-insensitive) key * only the last (according to the natural ordering of the supplied map) will remain * after the {@code putAll} method has returned. * * @param t the mappings to store in this result */ @SuppressWarnings("unchecked") public void putAll(Map t) { // don't delegate to putAll since we want case handling from put for (Entry next : (Set<Entry>) t.entrySet()) { put(next.getKey(), next.getValue()); } } public Object remove(Object rawKey) { return result.remove(lookupKeyIgnoringCase(rawKey)); } public int size() { return result.size(); } public Collection values() { return result.values(); } }