All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.impl.ResultImpl Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com)
 * All rights reserved.
 *
 * This work is dual-licensed
 * - under the Apache Software License 2.0 (the "ASL")
 * - under the jOOQ License and Maintenance Agreement (the "jOOQ License")
 * =============================================================================
 * You may choose which license applies to you:
 *
 * - If you're using this work with Open Source databases, you may choose
 *   either ASL or jOOQ License.
 * - If you're using this work with at least one commercial database, you must
 *   choose jOOQ License
 *
 * For more information, please visit http://www.jooq.org/licenses
 *
 * Apache Software License 2.0:
 * -----------------------------------------------------------------------------
 * Licensed 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.
 *
 * jOOQ License and Maintenance Agreement:
 * -----------------------------------------------------------------------------
 * Data Geekery grants the Customer the non-exclusive, timely limited and
 * non-transferable license to install and use the Software under the terms of
 * the jOOQ License and Maintenance Agreement.
 *
 * This library is distributed with a LIMITED WARRANTY. See the jOOQ License
 * and Maintenance Agreement for more details: http://www.jooq.org/licensing
 */

package org.jooq.impl;

import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.jooq.impl.Utils.indexOrFail;
import static org.jooq.tools.StringUtils.abbreviate;
import static org.jooq.tools.StringUtils.leftPad;
import static org.jooq.tools.StringUtils.rightPad;

import java.lang.reflect.Array;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.jooq.AttachableInternal;
import org.jooq.Configuration;
import org.jooq.Converter;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.RecordHandler;
import org.jooq.RecordMapper;
import org.jooq.RecordType;
import org.jooq.Result;
import org.jooq.Row;
import org.jooq.Table;
import org.jooq.exception.InvalidResultException;
import org.jooq.tools.Convert;
import org.jooq.tools.StringUtils;
import org.jooq.tools.jdbc.MockResultSet;
import org.jooq.tools.json.JSONObject;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
 * @author Lukas Eder
 * @author Ivan Dugic
 */
class ResultImpl implements Result, AttachableInternal {

    /**
     * Generated UID
     */
    private static final long serialVersionUID = 6416154375799578362L;

    private Configuration     configuration;
    private final Fields   fields;
    private final List     records;

    ResultImpl(Configuration configuration, Collection> fields) {
        this(configuration, new Fields(fields));
    }

    ResultImpl(Configuration configuration, Field... fields) {
        this(configuration, new Fields(fields));
    }

    ResultImpl(Configuration configuration, Fields fields) {
        this.configuration = configuration;
        this.fields = fields;
        this.records = new ArrayList();
    }

    // -------------------------------------------------------------------------
    // XXX: Attachable API
    // -------------------------------------------------------------------------

    @Override
    public final void attach(Configuration c) {
        this.configuration = c;

        for (R record : records) {
            if (record != null) {
                record.attach(c);
            }
        }
    }

    @Override
    public final void detach() {
        attach(null);
    }

    @Override
    public final Configuration configuration() {
        return configuration;
    }

    // -------------------------------------------------------------------------
    // XXX: Result API
    // -------------------------------------------------------------------------

    @Override
    public final RecordType recordType() {
        return fields;
    }

    @SuppressWarnings({ "rawtypes" })
    @Override
    public final Row fieldsRow() {
        return new RowImpl(fields);
    }

    @Override
    public final  Field field(Field field) {
        return fields.field(field);
    }

    @Override
    public final Field field(String name) {
        return fields.field(name);
    }

    @Override
    public final Field field(int index) {
        return fields.field(index);
    }

    @Override
    public final Field[] fields() {
        return fields.fields().clone();
    }

    @Override
    public final boolean isEmpty() {
        return records.isEmpty();
    }

    @Override
    public final boolean isNotEmpty() {
        return !records.isEmpty();
    }

    @Override
    public final  T getValue(int index, Field field) {
        return get(index).getValue(field);
    }

    @Override
    @Deprecated
    public final  T getValue(int index, Field field, T defaultValue) {
        return get(index).getValue(field, defaultValue);
    }

    @Override
    public final Object getValue(int index, int fieldIndex) {
        return get(index).getValue(fieldIndex);
    }

    @Override
    @Deprecated
    public final Object getValue(int index, int fieldIndex, Object defaultValue) {
        return get(index).getValue(fieldIndex, defaultValue);
    }

    @Override
    public final Object getValue(int index, String fieldName) {
        return get(index).getValue(fieldName);
    }

    @Override
    @Deprecated
    public final Object getValue(int index, String fieldName, Object defaultValue) {
        return get(index).getValue(fieldName, defaultValue);
    }

    @SuppressWarnings("unchecked")
    @Override
    public final  List getValues(Field field) {
        return (List) getValues(indexOrFail(fieldsRow(), field));
    }

    @Override
    public final  List getValues(Field field, Class type) {
        return Convert.convert(getValues(field), type);
    }

    @Override
    public final  List getValues(Field field, Converter converter) {
        return Convert.convert(getValues(field), converter);
    }

    @Override
    public final List getValues(int fieldIndex) {
        List result = new ArrayList(size());

        for (R record : this) {
            result.add(record.getValue(fieldIndex));
        }

        return result;
    }

    @Override
    public final  List getValues(int fieldIndex, Class type) {
        return Convert.convert(getValues(fieldIndex), type);
    }

    @Override
    public final  List getValues(int fieldIndex, Converter converter) {
        return Convert.convert(getValues(fieldIndex), converter);
    }

    @Override
    public final List getValues(String fieldName) {
        return getValues(field(fieldName));
    }

    @Override
    public final  List getValues(String fieldName, Class type) {
        return Convert.convert(getValues(fieldName), type);
    }

    @Override
    public final  List getValues(String fieldName, Converter converter) {
        return Convert.convert(getValues(fieldName), converter);
    }

    final void addRecord(R record) {
        records.add(record);
    }

    @Override
    public final String format() {
        return format(50);
    }

    @Override
    public final String format(int maxRecords) {
        final int COL_MIN_WIDTH = 4;
        final int COL_MAX_WIDTH = 50;

        // Numeric columns have greater max width because values are aligned
        final int NUM_COL_MAX_WIDTH = 100;

        // The max number of records that will be considered for formatting purposes
        final int MAX_RECORDS = min(50, maxRecords);

        // Get max decimal places for numeric type columns
        final int[] decimalPlaces = new int[fields.fields.length];
        final int[] widths = new int[fields.fields.length];

        for (int index = 0; index < fields.fields.length; index++) {
            if (Number.class.isAssignableFrom(fields.fields[index].getType())) {
                List decimalPlacesList = new ArrayList();

                // Initialize
                decimalPlacesList.add(0);

                // Collect all decimal places for the column values
                String value;
                for (int i = 0; i < min(MAX_RECORDS, size()); i++) {
                    value = format0(getValue(i, index), get(i).changed(index));
                    decimalPlacesList.add(getDecimalPlaces(value));
                }

                // Find max
                decimalPlaces[index] = Collections.max(decimalPlacesList);
            }
        }

        // Get max column widths
        int colMaxWidth;
        for (int index = 0; index < fields.fields.length; index++) {

            // Is number column?
            boolean isNumCol = Number.class.isAssignableFrom(fields.fields[index].getType());

            colMaxWidth = isNumCol ? NUM_COL_MAX_WIDTH : COL_MAX_WIDTH;

            // Collect all widths for the column
            List widthList = new ArrayList();

            // Add column name width first
            widthList.add(min(colMaxWidth, max(COL_MIN_WIDTH, fields.fields[index].getName().length())));

            // Add column values width
            String value;
            for (int i = 0; i < min(MAX_RECORDS, size()); i++) {
                value = format0(getValue(i, index), get(i).changed(index));
                // Align number values before width is calculated
                if (isNumCol) {
                    value = alignNumberValue(decimalPlaces[index], value);
                }

                widthList.add(min(colMaxWidth, value.length()));
            }

            // Find max
            widths[index] = Collections.max(widthList);
        }

        // Begin the writing
        // ---------------------------------------------------------------------
        StringBuilder sb = new StringBuilder();

        // Write top line
        sb.append("+");
        for (int index = 0; index < fields.fields.length; index++) {
            sb.append(rightPad("", widths[index], "-"));
            sb.append("+");
        }

        // Write headers
        sb.append("\n|");
        for (int index = 0; index < fields.fields.length; index++) {
            String padded;

            if (Number.class.isAssignableFrom(fields.fields[index].getType())) {
                padded = leftPad(fields.fields[index].getName(), widths[index]);
            }
            else {
                padded = rightPad(fields.fields[index].getName(), widths[index]);
            }

            sb.append(abbreviate(padded, widths[index]));
            sb.append("|");
        }

        // Write separator
        sb.append("\n+");
        for (int index = 0; index < fields.fields.length; index++) {
            sb.append(rightPad("", widths[index], "-"));
            sb.append("+");
        }

        // Write columns
        for (int i = 0; i < min(maxRecords, size()); i++) {
            sb.append("\n|");

            for (int index = 0; index < fields.fields.length; index++) {
                String value = format0(getValue(i, index), get(i).changed(index)).replace("\n", "{lf}").replace("\r", "{cr}");

                String padded;
                if (Number.class.isAssignableFrom(fields.fields[index].getType())) {
                    // Align number value before left pad
                    value = alignNumberValue(decimalPlaces[index], value);

                    // Left pad
                    padded = leftPad(value, widths[index]);
                }
                else {
                    // Right pad
                    padded = rightPad(value, widths[index]);
                }

                sb.append(abbreviate(padded, widths[index]));
                sb.append("|");
            }
        }

        // Write bottom line
        if (size() > 0) {
            sb.append("\n+");

            for (int index = 0; index < fields.fields.length; index++) {
                sb.append(rightPad("", widths[index], "-"));
                sb.append("+");
            }
        }

        // Write truncation message, if applicable
        if (maxRecords < size()) {
            sb.append("\n|...");
            sb.append(size() - maxRecords);
            sb.append(" record(s) truncated...");
        }

        return sb.toString();
    }

    private static final String alignNumberValue(Integer columnDecimalPlaces, String value) {
        if (!"{null}".equals(value) && columnDecimalPlaces != 0) {
            int decimalPlaces = getDecimalPlaces(value);
            int rightPadSize = value.length() + columnDecimalPlaces - decimalPlaces;

            if (decimalPlaces == 0) {
                // If integer value, add one for decimal point
                value = rightPad(value, rightPadSize + 1);
            }
            else {
                value = rightPad(value, rightPadSize);
            }
        }

        return value;
    }

    private static final int getDecimalPlaces(String value) {
        int decimalPlaces = 0;

        int dotIndex = value.indexOf(".");
        if (dotIndex != -1) {
            decimalPlaces = value.length() - dotIndex - 1;
        }

        return decimalPlaces;
    }

    @Override
    public final String formatHTML() {
        StringBuilder sb = new StringBuilder();

        sb.append("");
        sb.append("");
        sb.append("");

        for (Field field : fields.fields) {
            sb.append("");
        }

        sb.append("");
        sb.append("");
        sb.append("");

        for (Record record : this) {
            sb.append("");

            for (int index = 0; index < fields.fields.length; index++) {
                sb.append("");
            }

            sb.append("");
        }

        sb.append("");
        sb.append("
"); sb.append(field.getName()); sb.append("
"); sb.append(format0(record.getValue(index), false)); sb.append("
"); return sb.toString(); } @Override public final String formatCSV() { return formatCSV(',', ""); } @Override public final String formatCSV(char delimiter) { return formatCSV(delimiter, ""); } @Override public final String formatCSV(char delimiter, String nullString) { StringBuilder sb = new StringBuilder(); String sep1 = ""; for (Field field : fields.fields) { sb.append(sep1); sb.append(formatCSV0(field.getName(), "")); sep1 = Character.toString(delimiter); } sb.append("\n"); for (Record record : this) { String sep2 = ""; for (int index = 0; index < fields.fields.length; index++) { sb.append(sep2); sb.append(formatCSV0(record.getValue(index), nullString)); sep2 = Character.toString(delimiter); } sb.append("\n"); } return sb.toString(); } private final String formatCSV0(Object value, String nullString) { // Escape null and empty strings if (value == null || "".equals(value)) { if (StringUtils.isEmpty(nullString)) { return "\"\""; } else { return nullString; } } String result = format0(value, false); if (StringUtils.containsAny(result, ',', ';', '\t', '"', '\n', '\r', '\'', '\\')) { return "\"" + result.replace("\\", "\\\\").replace("\"", "\"\"") + "\""; } else { return result; } } private static final String format0(Object value, boolean changed) { String formatted = changed ? "*" : ""; if (value == null) { formatted += "{null}"; } else if (value.getClass() == byte[].class) { formatted += Arrays.toString((byte[]) value); } else if (value.getClass().isArray()) { formatted += Arrays.toString((Object[]) value); } else if (value instanceof EnumType) { formatted += ((EnumType) value).getLiteral(); } else { formatted += value.toString(); } return formatted; } @Override public final String formatJSON() { List> f = new ArrayList>(); List> r = new ArrayList>(); Map fieldMap; for (Field field : fields.fields) { fieldMap = new LinkedHashMap(); fieldMap.put("name", field.getName()); fieldMap.put("type", field.getDataType().getTypeName().toUpperCase()); f.add(fieldMap); } for (Record record : this) { List list = new ArrayList(); for (int index = 0; index < fields.fields.length; index++) { list.add(record.getValue(index)); } r.add(list); } Map> map = new LinkedHashMap>(); map.put("fields", f); map.put("records", r); return JSONObject.toJSONString(map); } @Override public final String formatXML() { StringBuilder sb = new StringBuilder(); sb.append(""); sb.append(""); for (Field field : fields.fields) { sb.append(""); } sb.append(""); sb.append(""); for (Record record : this) { sb.append(""); for (int index = 0; index < fields.fields.length; index++) { Object value = record.getValue(index); sb.append(""); } else { sb.append(">"); sb.append(escapeXML(format0(value, false))); sb.append(""); } } sb.append(""); } sb.append(""); sb.append(""); return sb.toString(); } @Override public final Document intoXML() { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.newDocument(); Element eResult = document.createElement("result"); eResult.setAttribute("xmlns", "http://www.jooq.org/xsd/jooq-export-2.6.0.xsd"); document.appendChild(eResult); Element eFields = document.createElement("fields"); eResult.appendChild(eFields); for (Field field : fields.fields) { Element eField = document.createElement("field"); eField.setAttribute("name", field.getName()); eField.setAttribute("type", field.getDataType().getTypeName().toUpperCase()); eFields.appendChild(eField); } Element eRecords = document.createElement("records"); eResult.appendChild(eRecords); for (Record record : this) { Element eRecord = document.createElement("record"); eRecords.appendChild(eRecord); for (int index = 0; index < fields.fields.length; index++) { Field field = fields.fields[index]; Object value = record.getValue(index); Element eValue = document.createElement("value"); eValue.setAttribute("field", field.getName()); eRecord.appendChild(eValue); if (value != null) { eValue.setTextContent(format0(value, false)); } } } return document; } catch (ParserConfigurationException ignore) { throw new RuntimeException(ignore); } } @Override public final H intoXML(H handler) throws SAXException { Attributes empty = new AttributesImpl(); handler.startDocument(); handler.startPrefixMapping("", "http://www.jooq.org/xsd/jooq-export-2.6.0.xsd"); handler.startElement("", "", "result", empty); handler.startElement("", "", "fields", empty); for (Field field : fields.fields) { AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("", "", "name", "CDATA", field.getName()); attrs.addAttribute("", "", "type", "CDATA", field.getDataType().getTypeName().toUpperCase()); handler.startElement("", "", "field", attrs); handler.endElement("", "", "field"); } handler.endElement("", "", "fields"); handler.startElement("", "", "records", empty); for (Record record : this) { handler.startElement("", "", "record", empty); for (int index = 0; index < fields.fields.length; index++) { Field field = fields.fields[index]; Object value = record.getValue(index); AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("", "", "field", "CDATA", field.getName()); handler.startElement("", "", "value", attrs); if (value != null) { char[] chars = format0(value, false).toCharArray(); handler.characters(chars, 0, chars.length); } handler.endElement("", "", "value"); } handler.endElement("", "", "record"); } handler.endElement("", "", "records"); handler.endPrefixMapping(""); handler.endDocument(); return handler; } private final String escapeXML(String string) { return StringUtils.replaceEach(string, new String[] { "\"", "'", "<", ">", "&" }, new String[] { """, "'", "<", ">", "&"}); } @Override public final List> intoMaps() { List> list = new ArrayList>(); for (R record : this) { list.add(record.intoMap()); } return list; } @SuppressWarnings("unchecked") @Override public final Map intoMap(Field key) { int index = indexOrFail(fieldsRow(), key); Map map = new LinkedHashMap(); for (R record : this) { if (map.put((K) record.getValue(index), record) != null) { throw new InvalidResultException("Key " + key + " is not unique in Result for " + this); } } return map; } @SuppressWarnings("unchecked") @Override public final Map intoMap(Field key, Field value) { int kIndex = indexOrFail(fieldsRow(), key); int vIndex = indexOrFail(fieldsRow(), value); Map map = new LinkedHashMap(); for (R record : this) { if (map.put((K) record.getValue(kIndex), (V) record.getValue(vIndex)) != null) { throw new InvalidResultException("Key " + key + " is not unique in Result for " + this); } } return map; } @Override public final Map intoMap(Field[] keys) { if (keys == null) { keys = new Field[0]; } Map map = new LinkedHashMap(); for (R record : this) { @SuppressWarnings({ "rawtypes", "unchecked" }) RecordImpl key = new RecordImpl(keys); for (Field field : keys) { Utils.copyValue(key, field, record, field); } if (map.put(key, record) != null) { throw new InvalidResultException("Key list " + Arrays.asList(keys) + " is not unique in Result for " + this); } } return map; } @Override public final Map, E> intoMap(Field[] keys, Class type) { return intoMap(keys, Utils.configuration(this).recordMapperProvider().provide(fields, type)); } @Override public final Map, E> intoMap(Field[] keys, RecordMapper mapper) { if (keys == null) { keys = new Field[0]; } Map, E> map = new LinkedHashMap, E>(); for (R record : this) { List keyValueList = new ArrayList(); for (Field key : keys) { keyValueList.add(record.getValue(key)); } if (map.put(keyValueList, mapper.map(record)) != null) { throw new InvalidResultException("Key list " + keyValueList + " is not unique in Result for " + this); } } return map; } @Override public final Map intoMap(Field key, Class type) { return intoMap(key, Utils.configuration(this).recordMapperProvider().provide(fields, type)); } @SuppressWarnings("unchecked") @Override public final Map intoMap(Field key, RecordMapper mapper) { int index = indexOrFail(fieldsRow(), key); Map map = new LinkedHashMap(); for (R record : this) { if (map.put((K) record.getValue(index), mapper.map(record)) != null) { throw new InvalidResultException("Key " + key + " is not unique in Result for " + this); } } return map; } @SuppressWarnings("unchecked") @Override public final Map> intoGroups(Field key) { int index = indexOrFail(fieldsRow(), key); Map> map = new LinkedHashMap>(); for (R record : this) { K val = (K) record.getValue(index); Result result = map.get(val); if (result == null) { result = new ResultImpl(configuration, fields); map.put(val, result); } result.add(record); } return map; } @SuppressWarnings("unchecked") @Override public final Map> intoGroups(Field key, Field value) { int kIndex = indexOrFail(fieldsRow(), key); int vIndex = indexOrFail(fieldsRow(), value); Map> map = new LinkedHashMap>(); for (R record : this) { K k = (K) record.getValue(kIndex); V v = (V) record.getValue(vIndex); List result = map.get(k); if (result == null) { result = new ArrayList(); map.put(k, result); } result.add(v); } return map; } @Override public final Map> intoGroups(Field[] keys) { if (keys == null) { keys = new Field[0]; } Map> map = new LinkedHashMap>(); for (R record : this) { @SuppressWarnings({ "rawtypes", "unchecked" }) RecordImpl key = new RecordImpl(keys); for (Field field : keys) { Utils.copyValue(key, field, record, field); } Result result = map.get(key); if (result == null) { result = new ResultImpl(configuration(), this.fields); map.put(key, result); } result.add(record); } return map; } @Override public final Map> intoGroups(Field key, Class type) { return intoGroups(key, Utils.configuration(this).recordMapperProvider().provide(fields, type)); } @SuppressWarnings("unchecked") @Override public final Map> intoGroups(Field key, RecordMapper mapper) { int index = indexOrFail(fieldsRow(), key); Map> map = new LinkedHashMap>(); for (R record : this) { K keyVal = (K) record.getValue(index); List list = map.get(keyVal); if (list == null) { list = new ArrayList(); map.put(keyVal, list); } list.add(mapper.map(record)); } return map; } @Override public final Map> intoGroups(Field[] keys, Class type) { return intoGroups(keys, Utils.configuration(this).recordMapperProvider().provide(fields, type)); } @Override public final Map> intoGroups(Field[] keys, RecordMapper mapper) { if (keys == null) { keys = new Field[0]; } Map> map = new LinkedHashMap>(); for (R record : this) { @SuppressWarnings({ "rawtypes", "unchecked" }) RecordImpl key = new RecordImpl(keys); for (Field field : keys) { Utils.copyValue(key, field, record, field); } List list = map.get(key); if (list == null) { list = new ArrayList(); map.put(key, list); } list.add(mapper.map(record)); } return map; } @Override public final Object[][] intoArray() { int size = size(); Object[][] array = new Object[size][]; for (int i = 0; i < size; i++) { array[i] = get(i).intoArray(); } return array; } @Override public final Object[] intoArray(int fieldIndex) { Class type = fields.fields[fieldIndex].getType(); List list = getValues(fieldIndex); return list.toArray((Object[]) Array.newInstance(type, list.size())); } @SuppressWarnings("unchecked") @Override public final T[] intoArray(int fieldIndex, Class type) { return (T[]) Convert.convertArray(intoArray(fieldIndex), type); } @Override public final U[] intoArray(int fieldIndex, Converter converter) { return Convert.convertArray(intoArray(fieldIndex), converter); } @Override public final Object[] intoArray(String fieldName) { Class type = field(fieldName).getType(); List list = getValues(fieldName); return list.toArray((Object[]) Array.newInstance(type, list.size())); } @SuppressWarnings("unchecked") @Override public final T[] intoArray(String fieldName, Class type) { return (T[]) Convert.convertArray(intoArray(fieldName), type); } @Override public final U[] intoArray(String fieldName, Converter converter) { return Convert.convertArray(intoArray(fieldName), converter); } @SuppressWarnings("unchecked") @Override public final T[] intoArray(Field field) { return getValues(field).toArray((T[]) Array.newInstance(field.getType(), 0)); } @SuppressWarnings("unchecked") @Override public final T[] intoArray(Field field, Class type) { return (T[]) Convert.convertArray(intoArray(field), type); } @Override public final U[] intoArray(Field field, Converter converter) { return Convert.convertArray(intoArray(field), converter); } @Override public final List into(Class type) { List list = new ArrayList(size()); RecordMapper mapper = Utils.configuration(this).recordMapperProvider().provide(fields, type); for (R record : this) { list.add(mapper.map(record)); } return list; } @Override public final Result into(Table table) { Result list = new ResultImpl(configuration(), table.fields()); for (R record : this) { list.add(record.into(table)); } return list; } @Override public final > H into(H handler) { for (R record : this) { handler.next(record); } return handler; } @Override public final ResultSet intoResultSet() { return new MockResultSet(this); } @Override public final List map(RecordMapper mapper) { List result = new ArrayList(); for (R record : this) { result.add(mapper.map(record)); } return result; } @Override public final > Result sortAsc(Field field) { return sortAsc(field, new NaturalComparator()); } @SuppressWarnings("rawtypes") @Override public final Result sortAsc(int fieldIndex) { return sortAsc(fieldIndex, new NaturalComparator()); } @SuppressWarnings("rawtypes") @Override public final Result sortAsc(String fieldName) { return sortAsc(fieldName, new NaturalComparator()); } @Override public final Result sortAsc(Field field, Comparator comparator) { return sortAsc(indexOrFail(fieldsRow(), field), comparator); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public final Result sortAsc(int fieldIndex, Comparator comparator) { return sortAsc(new RecordComparator(fieldIndex, comparator)); } @Override public final Result sortAsc(String fieldName, Comparator comparator) { return sortAsc(indexOrFail(fieldsRow(), fieldName), comparator); } @Override public final Result sortAsc(Comparator comparator) { Collections.sort(this, comparator); return this; } @Override public final > Result sortDesc(Field field) { return sortAsc(field, Collections.reverseOrder(new NaturalComparator())); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public final Result sortDesc(int fieldIndex) { return sortAsc(fieldIndex, Collections.reverseOrder(new NaturalComparator())); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public final Result sortDesc(String fieldName) { return sortAsc(fieldName, Collections.reverseOrder(new NaturalComparator())); } @Override public final Result sortDesc(Field field, Comparator comparator) { return sortAsc(field, Collections.reverseOrder(comparator)); } @Override public final Result sortDesc(int fieldIndex, Comparator comparator) { return sortAsc(fieldIndex, Collections.reverseOrder(comparator)); } @Override public final Result sortDesc(String fieldName, Comparator comparator) { return sortAsc(fieldName, Collections.reverseOrder(comparator)); } @Override public final Result sortDesc(Comparator comparator) { return sortAsc(Collections.reverseOrder(comparator)); } @Override public final Result intern(Field... f) { return intern(fields.indexesOf(f)); } @Override public final Result intern(int... fieldIndexes) { for (int fieldIndex : fieldIndexes) { if (fields.fields[fieldIndex].getType() == String.class) { for (Record record : this) { ((AbstractRecord) record).getValue0(fieldIndex).intern(); } } } return this; } @Override public final Result intern(String... fieldNames) { return intern(fields.indexesOf(fieldNames)); } /** * A comparator for records, wrapping another comparator for <T> */ private static class RecordComparator implements Comparator { private final Comparator comparator; private final int fieldIndex; RecordComparator(int fieldIndex, Comparator comparator) { this.fieldIndex = fieldIndex; this.comparator = comparator; } @SuppressWarnings("unchecked") @Override public int compare(R record1, R record2) { return comparator.compare((T) record1.getValue(fieldIndex), (T) record2.getValue(fieldIndex)); } } /** * A natural comparator */ private static class NaturalComparator> implements Comparator { @Override public int compare(T o1, T o2) { if (o1 == null && o2 == null) { return 0; } else if (o1 == null) { return -1; } else if (o2 == null) { return 1; } return o1.compareTo(o2); } } // ------------------------------------------------------------------------- // XXX Object API // ------------------------------------------------------------------------- @Override public String toString() { return format(); } @Override public int hashCode() { return records.hashCode(); } @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof ResultImpl) { ResultImpl other = (ResultImpl) obj; return records.equals(other.records); } return false; } // ------------------------------------------------------------------------- // XXX: List API // ------------------------------------------------------------------------- @Override public final int size() { return records.size(); } @Override public final boolean contains(Object o) { return records.contains(o); } @Override public final Object[] toArray() { return records.toArray(); } @Override public final T[] toArray(T[] a) { return records.toArray(a); } @Override public final boolean add(R e) { return records.add(e); } @Override public final boolean remove(Object o) { return records.remove(o); } @Override public final boolean containsAll(Collection c) { return records.containsAll(c); } @Override public final boolean addAll(Collection c) { return records.addAll(c); } @Override public final boolean addAll(int index, Collection c) { return records.addAll(index, c); } @Override public final boolean removeAll(Collection c) { return records.removeAll(c); } @Override public final boolean retainAll(Collection c) { return records.retainAll(c); } @Override public final void clear() { records.clear(); } @Override public final R get(int index) { return records.get(index); } @Override public final R set(int index, R element) { return records.set(index, element); } @Override public final void add(int index, R element) { records.add(index, element); } @Override public final R remove(int index) { return records.remove(index); } @Override public final int indexOf(Object o) { return records.indexOf(o); } @Override public final int lastIndexOf(Object o) { return records.lastIndexOf(o); } @Override public final Iterator iterator() { return records.iterator(); } @Override public final ListIterator listIterator() { return records.listIterator(); } @Override public final ListIterator listIterator(int index) { return records.listIterator(index); } @Override public final List subList(int fromIndex, int toIndex) { return records.subList(fromIndex, toIndex); } }