View Javadoc
1   package com.opencsv;
2   
3   /*
4    * Copyright 2015 Scott Conway
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import org.apache.commons.lang3.ArrayUtils;
20  import org.apache.commons.lang3.ObjectUtils;
21  import org.apache.commons.lang3.StringUtils;
22  
23  import java.io.IOException;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.util.*;
27  import java.util.stream.Collectors;
28  import java.util.stream.Stream;
29  
30  /**
31   * Helper class for processing JDBC ResultSet objects allowing the user to
32   * process a subset of columns and set custom header names.
33   */
34  public class ResultSetColumnNameHelperService extends ResultSetHelperService implements ResultSetHelper {
35      private String[] columnNames;
36      private String[] columnHeaders;
37      private final Map<String, Integer> columnNamePositionMap = new HashMap<>();
38      private Locale errorLocale = Locale.getDefault();
39  
40      /**
41       * Nullary constructor.
42       */
43      public ResultSetColumnNameHelperService() {
44      }
45      
46      /**
47       * Sets the locale for error messages.
48       * @param errorLocale Locale for error messages. If null, the default locale
49       *   is used.
50       * @since 4.0
51       */
52      public void setErrorLocale(Locale errorLocale) {
53          this.errorLocale = ObjectUtils.defaultIfNull(errorLocale, Locale.getDefault());
54      }
55      
56      /**
57       * Set the JDBC column names to use, and the header text for the CSV file
58       * @param columnNames The JDBC column names to export, in the desired order
59       * @param columnHeaders The column headers of the CSV file, in the desired order
60       * @throws UnsupportedOperationException If the number of headers is different
61       * than the number of columns, or if any of the columns or headers is blank
62       * or null.
63       */
64      public void setColumnNames(String[] columnNames, String[] columnHeaders) {
65          if (columnHeaders.length != columnNames.length) {
66              throw new UnsupportedOperationException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("column.count.mismatch"));
67          }
68          if (hasInvalidValue(columnNames)) {
69              throw new UnsupportedOperationException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("column.name.bogus"));
70          }
71          if (hasInvalidValue(columnHeaders)) {
72              throw new UnsupportedOperationException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("header.name.bogus"));
73          }
74          this.columnNames = Arrays.copyOf(columnNames, columnNames.length);
75          this.columnHeaders = Arrays.copyOf(columnHeaders, columnHeaders.length);
76      }
77  
78      private boolean hasInvalidValue(String[] strings) {
79          return Stream.of(strings).anyMatch(s -> StringUtils.isBlank(s));
80      }
81  
82      /**
83       * Returns the column names from the result set.
84       * @param rs ResultSet
85       * @return A string array containing the column names.
86       * @throws SQLException Thrown by the result set.
87       */
88      @Override
89      public String[] getColumnNames(ResultSet rs) throws SQLException {
90          if (columnNamePositionMap.isEmpty()) {
91              populateColumnData(rs);
92          }
93          return Arrays.copyOf(columnHeaders, columnHeaders.length);
94      }
95  
96      private void populateColumnData(ResultSet rs) throws SQLException {
97          String[] realColumnNames = super.getColumnNames(rs);
98  
99          if (columnNames == null) {
100             columnNames = Arrays.copyOf(realColumnNames, realColumnNames.length);
101             columnHeaders = Arrays.copyOf(realColumnNames, realColumnNames.length);
102         }
103 
104         for (String name : columnNames) {
105             int position = ArrayUtils.indexOf(realColumnNames, name);
106             if (position == ArrayUtils.INDEX_NOT_FOUND) {
107                 throw new UnsupportedOperationException(String.format(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("column.nonexistant"), name));
108             }
109             columnNamePositionMap.put(name, position);
110         }
111     }
112 
113     /**
114      * Get all the column values from the result set.
115      * @param rs The ResultSet containing the values.
116      * @return String array containing all the column values.
117      * @throws SQLException Thrown by the result set.
118      * @throws IOException Thrown by the result set.
119      */
120     @Override
121     public String[] getColumnValues(ResultSet rs) throws SQLException, IOException {
122         if (columnNamePositionMap.isEmpty()) {
123             populateColumnData(rs);
124         }
125         String[] realColumnValues = super.getColumnValues(rs, false, dateFormat, dateTimeFormat);
126         return getColumnValueSubset(realColumnValues);
127     }
128 
129     /**
130      * Get all the column values from the result set.
131      * @param rs The ResultSet containing the values.
132      * @param trim Values should have white spaces trimmed.
133      * @return String array containing all the column values.
134      * @throws SQLException Thrown by the result set.
135      * @throws IOException Thrown by the result set.
136      */
137     @Override
138     public String[] getColumnValues(ResultSet rs, boolean trim) throws SQLException, IOException {
139         if (columnNamePositionMap.isEmpty()) {
140             populateColumnData(rs);
141         }
142         String[] realColumnValues = super.getColumnValues(rs, trim, dateFormat, dateTimeFormat);
143         return getColumnValueSubset(realColumnValues);
144     }
145 
146     /**
147      * Get all the column values from the result set.
148      * @param rs The ResultSet containing the values.
149      * @param trim Values should have white spaces trimmed.
150      * @param dateFormatString Format string for dates.
151      * @param timeFormatString Format string for timestamps.
152      * @return String array containing all the column values.
153      * @throws SQLException Thrown by the result set.
154      * @throws IOException Thrown by the result set.
155      */
156     @Override
157     public String[] getColumnValues(ResultSet rs, boolean trim, String dateFormatString, String timeFormatString) throws SQLException, IOException {
158         if (columnNamePositionMap.isEmpty()) {
159             populateColumnData(rs);
160         }
161         String[] realColumnValues = super.getColumnValues(rs, trim, dateFormatString, timeFormatString);
162         return getColumnValueSubset(realColumnValues);
163     }
164 
165     private String[] getColumnValueSubset(String[] realColumnValues) {
166         return Stream.of(columnNames)
167                 .map(c -> realColumnValues[columnNamePositionMap.get(c)])
168                 .collect(Collectors.toList())
169                 .toArray(ArrayUtils.EMPTY_STRING_ARRAY);
170     }
171 }