@Override public final void from(Object source, Field<?>... f) { if (source == null) return; // [#1987] Distinguish between various types to load data from // Maps are loaded using a {field-name -> value} convention if (source instanceof Map) { fromMap((Map<String, ?>) source, f); } // Arrays are loaded through index mapping else if (source instanceof Object[]) { fromArray((Object[]) source, f); } // All other types are expected to be POJOs else { Class<?> type = source.getClass(); try { boolean useAnnotations = hasColumnAnnotations(configuration(), type); for (Field<?> field : f) { List<java.lang.reflect.Field> members; Method method; // Annotations are available and present if (useAnnotations) { members = getAnnotatedMembers(configuration(), type, field.getName()); method = getAnnotatedGetter(configuration(), type, field.getName()); } // No annotations are present else { members = getMatchingMembers(configuration(), type, field.getName()); method = getMatchingGetter(configuration(), type, field.getName()); } // Use only the first applicable method or member if (method != null) { Utils.setValue(this, field, method.invoke(source)); } else if (members.size() > 0) { from(source, members.get(0), field); } } } // All reflection exceptions are intercepted catch (Exception e) { throw new MappingException("An error ocurred when mapping record from " + type, e); } } }
@Override public final Document intoXML() { final int size = getFields().size(); 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 : getFields()) { 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 < size; index++) { Field<?> field = getField(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)); } } } return document; } catch (ParserConfigurationException ignore) { throw new RuntimeException(ignore); } }
@Override public final String formatXML() { final int size = getFields().size(); StringBuilder sb = new StringBuilder(); sb.append("<result xmlns=\"http://www.jooq.org/xsd/jooq-export-2.6.0.xsd\">"); sb.append("<fields>"); for (Field<?> field : getFields()) { sb.append("<field name=\""); sb.append(escapeXML(field.getName())); sb.append("\" "); sb.append("type=\""); sb.append(field.getDataType().getTypeName().toUpperCase()); sb.append("\"/>"); } sb.append("</fields>"); sb.append("<records>"); for (Record record : this) { sb.append("<record>"); for (int index = 0; index < size; index++) { Field<?> field = getField(index); Object value = record.getValue(index); sb.append("<value field=\""); sb.append(escapeXML(field.getName())); sb.append("\""); if (value == null) { sb.append("/>"); } else { sb.append(">"); sb.append(escapeXML(format0(value))); sb.append("</value>"); } } sb.append("</record>"); } sb.append("</records>"); sb.append("</result>"); return sb.toString(); }
@Override public final Map<String, Object> intoMap() { Map<String, Object> map = new LinkedHashMap<String, Object>(); int size = fields.size(); for (int i = 0; i < size; i++) { Field<?> field = fields.field(i); if (map.put(field.getName(), getValue(i)) != null) { throw new InvalidResultException( "Field " + field.getName() + " is not unique in Record : " + this); } } return map; }
@Override public final String formatJSON() { List<Map<String, String>> f = new ArrayList<Map<String, String>>(); List<List<Object>> r = new ArrayList<List<Object>>(); Map<String, String> fieldMap; for (Field<?> field : fields) { fieldMap = new LinkedHashMap<String, String>(); fieldMap.put("name", field.getName()); fieldMap.put("type", field.getDataType().getTypeName().toUpperCase()); f.add(fieldMap); } for (Record record : this) { List<Object> list = new ArrayList<Object>(); for (int index = 0; index < fields.fields.length; index++) { list.add(record.getValue(index)); } r.add(list); } Map<String, List<?>> map = new LinkedHashMap<String, List<?>>(); map.put("fields", f); map.put("records", r); return JSONObject.toJSONString(map); }
@Override public final String formatCSV(char delimiter, String nullString) { StringBuilder sb = new StringBuilder(); String sep1 = ""; for (Field<?> field : 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(); }
@Override public final String formatHTML() { StringBuilder sb = new StringBuilder(); sb.append("<table>"); sb.append("<thead>"); sb.append("<tr>"); for (Field<?> field : fields) { sb.append("<th>"); sb.append(field.getName()); sb.append("</th>"); } sb.append("</tr>"); sb.append("</thead>"); sb.append("<tbody>"); for (Record record : this) { sb.append("<tr>"); for (int index = 0; index < fields.fields.length; index++) { sb.append("<td>"); sb.append(format0(record.getValue(index), false)); sb.append("</td>"); } sb.append("</tr>"); } sb.append("</tbody>"); sb.append("</table>"); return sb.toString(); }
private String[] getTableColumnNames() { if (databaseDescriptor == null) { return new String[0]; } Set<String> columnNameSet = new HashSet<String>(); for (Table<?> table : databaseDescriptor.getSchema().getTables()) { for (Field<?> field : table.getFields()) { String columnName = field.getName(); columnNameSet.add(columnName); } } String[] columnNames = columnNameSet.toArray(new String[0]); Arrays.sort(columnNames, String.CASE_INSENSITIVE_ORDER); return columnNames; }
private static final Fields init(String alias, Class<?> arrayType) { List<Field<?>> result = new ArrayList<Field<?>>(); // [#1114] VARRAY/TABLE of OBJECT have more than one field if (UDTRecord.class.isAssignableFrom(arrayType)) { try { UDTRecord<?> record = (UDTRecord<?>) arrayType.newInstance(); for (Field<?> f : record.fields()) { result.add(fieldByName(f.getDataType(), alias, f.getName())); } } catch (Exception e) { throw new DataTypeException("Bad UDT Type : " + arrayType, e); } } // Simple array types have a synthetic field called "COLUMN_VALUE" else { result.add(fieldByName(Factory.getDataType(arrayType), alias, "COLUMN_VALUE")); } return new Fields(result); }
ImmutablePOJOMapperWithConstructorProperties( Constructor<E> constructor, ConstructorProperties properties) { this.constructor = constructor; this.propertyNames = Arrays.asList(properties.value()); this.useAnnotations = hasColumnAnnotations(configuration, type); this.parameterTypes = constructor.getParameterTypes(); this.parameterValues = new Object[parameterTypes.length]; this.members = new List[fields.length]; this.methods = new Method[fields.length]; this.propertyIndexes = new Integer[fields.length]; for (int i = 0; i < fields.length; i++) { Field<?> field = fields[i]; String name = field.getName(); String nameLC = StringUtils.toCamelCaseLC(name); // Annotations are available and present if (useAnnotations) { members[i] = getAnnotatedMembers(configuration, type, name); methods[i] = getAnnotatedGetter(configuration, type, name); } // No annotations are present else { members[i] = getMatchingMembers(configuration, type, name); methods[i] = getMatchingGetter(configuration, type, name); } // [#3911] Liberal interpretation of the @ConstructorProperties specs: // We also accept properties that don't have a matching getter or member for (int j = 0; j < propertyNames.size(); j++) { if (name.equals(propertyNames.get(j)) || nameLC.equals(propertyNames.get(j))) { propertyIndexes[i] = j; break; } } } }
@Override public final String formatHTML() { final int size = getFields().size(); StringBuilder sb = new StringBuilder(); sb.append("<table>"); sb.append("<thead>"); sb.append("<tr>"); for (Field<?> field : getFields()) { sb.append("<th>"); sb.append(field.getName()); sb.append("</th>"); } sb.append("</tr>"); sb.append("</thead>"); sb.append("<tbody>"); for (Record record : this) { sb.append("<tr>"); for (int index = 0; index < size; index++) { sb.append("<td>"); sb.append(format0(record.getValue(index))); sb.append("</td>"); } sb.append("</tr>"); } sb.append("</tbody>"); sb.append("</table>"); return sb.toString(); }
@Override protected final void prepare(ExecuteContext ctx) throws SQLException { Connection connection = ctx.connection(); // Just in case, always set Sybase ASE statement mode to return // Generated keys if client code wants to SELECT @@identity afterwards if (ctx.configuration().dialect() == SQLDialect.ASE) { ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; } // Normal statement preparing if no values should be returned else if (returning.isEmpty()) { super.prepare(ctx); return; } // Values should be returned from the INSERT else { switch (ctx.configuration().dialect()) { // Postgres uses the RETURNING clause in SQL case FIREBIRD: case POSTGRES: // SQLite will select last_insert_rowid() after the INSER case SQLITE: // Sybase will select @@identity after the INSERT case CUBRID: case SYBASE: super.prepare(ctx); return; // Some dialects can only return AUTO_INCREMENT values // Other values have to be fetched in a second step // [#1260] TODO CUBRID supports this, but there's a JDBC bug case ASE: case DERBY: case H2: case INGRES: case MYSQL: case SQLSERVER: ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; // The default is to return all requested fields directly case DB2: case HSQLDB: case ORACLE: default: { List<String> names = new ArrayList<String>(); for (Field<?> field : returning) { names.add(field.getName()); } ctx.statement( connection.prepareStatement(ctx.sql(), names.toArray(new String[names.size()]))); return; } } } }
MutablePOJOMapper(Constructor<? extends E> constructor, E instance) { this.constructor = accessible(constructor); this.useAnnotations = hasColumnAnnotations(configuration, type); this.members = new List[fields.length]; this.methods = new List[fields.length]; this.nested = new HashMap<String, List<RecordMapper<R, Object>>>(); this.instance = instance; Map<String, Field<?>[]> nestedFields = new HashMap<String, Field<?>[]>(); for (int i = 0; i < fields.length; i++) { Field<?> field = fields[i]; String name = field.getName(); // Annotations are available and present if (useAnnotations) { members[i] = getAnnotatedMembers(configuration, type, name); methods[i] = getAnnotatedSetters(configuration, type, name); } // No annotations are present else { int dot = name.indexOf('.'); // A nested mapping is applied if (dot > -1) { String prefix = name.substring(0, dot); Field<?>[] f = nestedFields.get(prefix); if (f == null) { f = nCopies(fields.length, field("")).toArray(new Field[fields.length]); nestedFields.put(prefix, f); } f[i] = field(name(name.substring(prefix.length() + 1)), field.getDataType()); members[i] = Collections.emptyList(); methods[i] = Collections.emptyList(); } // A top-level mapping is applied else { members[i] = getMatchingMembers(configuration, type, name); methods[i] = getMatchingSetters(configuration, type, name); } } } for (Entry<String, Field<?>[]> entry : nestedFields.entrySet()) { String prefix = entry.getKey(); List<RecordMapper<R, Object>> list = new ArrayList<RecordMapper<R, Object>>(); for (java.lang.reflect.Field member : getMatchingMembers(configuration, type, prefix)) { list.add( new RemovingPrefixRecordMapper( new DefaultRecordMapper<R, Object>( new Fields<R>(entry.getValue()), member.getType(), null, configuration), fields, prefix)); } for (Method method : getMatchingSetters(configuration, type, prefix)) { list.add( new RemovingPrefixRecordMapper( new DefaultRecordMapper<R, Object>( new Fields<R>(entry.getValue()), method.getParameterTypes()[0], null, configuration), fields, prefix)); } nested.put(prefix, list); } }
Neg(Field<T> field, ExpressionOperator operator) { super(operator.toSQL() + field.getName(), field.getDataType()); this.operator = operator; this.field = field; }
@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 size = getFields().size(); final int[] decimalPlaces = new int[size]; final int[] widths = new int[size]; for (int index = 0; index < size; index++) { Field<?> f = getField(index); if (Number.class.isAssignableFrom(f.getType())) { List<Integer> decimalPlacesList = new ArrayList<Integer>(); // 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)); decimalPlacesList.add(getDecimalPlaces(value)); } // Find max decimalPlaces[index] = Collections.max(decimalPlacesList); } } // Get max column widths int colMaxWidth; for (int index = 0; index < size; index++) { Field<?> f = getField(index); // Is number column? boolean isNumCol = Number.class.isAssignableFrom(f.getType()); colMaxWidth = isNumCol ? NUM_COL_MAX_WIDTH : COL_MAX_WIDTH; // Collect all widths for the column List<Integer> widthList = new ArrayList<Integer>(); // Add column name width first widthList.add(min(colMaxWidth, max(COL_MIN_WIDTH, f.getName().length()))); // Add column values width String value; for (int i = 0; i < min(MAX_RECORDS, size()); i++) { value = format0(getValue(i, 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 < size; index++) { sb.append(rightPad("", widths[index], "-")); sb.append("+"); } // Write headers sb.append("\n|"); for (int index = 0; index < size; index++) { Field<?> f = getField(index); String padded; if (Number.class.isAssignableFrom(f.getType())) { padded = leftPad(f.getName(), widths[index]); } else { padded = rightPad(f.getName(), widths[index]); } sb.append(abbreviate(padded, widths[index])); sb.append("|"); } // Write separator sb.append("\n+"); for (int index = 0; index < size; 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 < size; index++) { Field<?> f = getField(index); String value = format0(getValue(i, index)).replace("\n", "{lf}").replace("\r", "{cr}"); String padded; if (Number.class.isAssignableFrom(f.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 < size; 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 Table<Record> select(Configuration configuration) { List<Field<?>> groupingFields = new ArrayList<Field<?>>(); List<Field<?>> aliasedGroupingFields = new ArrayList<Field<?>>(); List<Field<?>> aggregatedFields = new ArrayList<Field<?>>(); Table<?> pivot = table.as("pivot_outer"); // Clearly, the API should be improved to make this more object- // oriented... // This loop finds all fields that are used in aggregate // functions. They're excluded from the GROUP BY clause for (Field<?> field : aggregateFunctions) { if (field instanceof Function) { for (QueryPart argument : ((Function<?>) field).getArguments()) { if (argument instanceof Field) { aggregatedFields.add((Field<?>) argument); } } } } // This loop finds all fields qualify for GROUP BY clauses for (Field<?> field : table.fields()) { if (!aggregatedFields.contains(field)) { if (!on.equals(field)) { aliasedGroupingFields.add(pivot.field(field)); groupingFields.add(field); } } } // The product {aggregateFunctions} x {in} List<Field<?>> aggregationSelects = new ArrayList<Field<?>>(); for (Field<?> inField : in) { for (Field<?> aggregateFunction : aggregateFunctions) { Condition join = trueCondition(); for (Field<?> field : groupingFields) { join = join.and(condition(pivot, field)); } @SuppressWarnings("unchecked") Select<?> aggregateSelect = using(configuration) .select(aggregateFunction) .from(table) .where(on.equal((Field<T>) inField)) .and(join); aggregationSelects.add( aggregateSelect.asField(inField.getName() + "_" + aggregateFunction.getName())); } } // This is the complete select Table<Record> select = using(configuration) .select(aliasedGroupingFields) .select(aggregationSelects) .from(pivot) .where(pivot.field(on).in(in.toArray(new Field[0]))) .groupBy(aliasedGroupingFields) .asTable(); return select; }